252 lines
8.6 KiB
Text
252 lines
8.6 KiB
Text
package templates
|
|
|
|
import (
|
|
"backend/internal/db/sqlc"
|
|
"backend/internal/web/ui"
|
|
"strconv"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
templ EtapeStrip(tabloID uuid.UUID, etapes []sqlc.Etape, counts EtapeTaskCounts, filter EtapeFilter, csrfToken string, oob bool) {
|
|
<div
|
|
id="etape-strip"
|
|
class="mb-4 space-y-3"
|
|
if oob {
|
|
hx-swap-oob="outerHTML"
|
|
}
|
|
>
|
|
<div class="flex items-center gap-2 overflow-x-auto pb-1">
|
|
<a
|
|
href={ templ.SafeURL("/tablos/" + tabloID.String() + "/tasks") }
|
|
hx-get={ "/tablos/" + tabloID.String() + "/tasks" }
|
|
hx-target="#tab-content"
|
|
hx-swap="innerHTML"
|
|
hx-push-url={ "/tablos/" + tabloID.String() + "/tasks" }
|
|
class={ etapeChipClasses(filter.IsAll()) }
|
|
>
|
|
<span>All</span>
|
|
<span class="rounded bg-slate-100 px-1.5 py-0.5 text-xs text-slate-700">{ strconv.Itoa(counts.All) }</span>
|
|
</a>
|
|
<a
|
|
href={ templ.SafeURL("/tablos/" + tabloID.String() + "/tasks?etape=unassigned") }
|
|
hx-get={ "/tablos/" + tabloID.String() + "/tasks?etape=unassigned" }
|
|
hx-target="#tab-content"
|
|
hx-swap="innerHTML"
|
|
hx-push-url={ "/tablos/" + tabloID.String() + "/tasks?etape=unassigned" }
|
|
class={ etapeChipClasses(filter.IsUnassigned()) }
|
|
>
|
|
<span>Unassigned</span>
|
|
<span class="rounded bg-slate-100 px-1.5 py-0.5 text-xs text-slate-700">{ strconv.Itoa(counts.Unassigned) }</span>
|
|
</a>
|
|
for index, etape := range etapes {
|
|
<div class="inline-flex flex-shrink-0 items-center gap-1">
|
|
<a
|
|
href={ templ.SafeURL("/tablos/" + tabloID.String() + "/tasks?etape=" + etape.ID.String()) }
|
|
hx-get={ "/tablos/" + tabloID.String() + "/tasks?etape=" + etape.ID.String() }
|
|
hx-target="#tab-content"
|
|
hx-swap="innerHTML"
|
|
hx-push-url={ "/tablos/" + tabloID.String() + "/tasks?etape=" + etape.ID.String() }
|
|
class={ etapeChipClasses(filter.IsEtape(etape.ID)) }
|
|
>
|
|
<span>{ etape.Title }</span>
|
|
<span class="rounded bg-slate-100 px-1.5 py-0.5 text-xs text-slate-700">{ strconv.Itoa(etapeCount(counts, etape.ID)) }</span>
|
|
</a>
|
|
<button
|
|
type="button"
|
|
class="ui-button ui-button-soft-neutral-md px-2"
|
|
hx-get={ "/tablos/" + tabloID.String() + "/etapes/" + etape.ID.String() + "/edit" }
|
|
hx-target="#etape-form-slot"
|
|
hx-swap="innerHTML"
|
|
aria-label={ "Edit etape: " + etape.Title }
|
|
>Edit</button>
|
|
<button
|
|
type="button"
|
|
class="ui-button ui-button-soft-danger-md px-2"
|
|
hx-get={ "/tablos/" + tabloID.String() + "/etapes/" + etape.ID.String() + "/delete-confirm" }
|
|
hx-target="#etape-form-slot"
|
|
hx-swap="innerHTML"
|
|
aria-label={ "Delete etape: " + etape.Title }
|
|
>Delete</button>
|
|
if index > 0 {
|
|
<form method="POST" action={ templ.SafeURL("/tablos/" + tabloID.String() + "/etapes/reorder") } hx-post={ "/tablos/" + tabloID.String() + "/etapes/reorder" } hx-target="#tasks-tab" hx-swap="outerHTML" class="inline">
|
|
@ui.CSRFField(csrfToken)
|
|
for _, id := range etapeReorderIDs(etapes, index, -1) {
|
|
<input type="hidden" name="etape_id" value={ id.String() }/>
|
|
}
|
|
<button type="submit" class="ui-button ui-button-soft-neutral-md px-2" aria-label={ "Move etape earlier: " + etape.Title }>Up</button>
|
|
</form>
|
|
}
|
|
if index < len(etapes)-1 {
|
|
<form method="POST" action={ templ.SafeURL("/tablos/" + tabloID.String() + "/etapes/reorder") } hx-post={ "/tablos/" + tabloID.String() + "/etapes/reorder" } hx-target="#tasks-tab" hx-swap="outerHTML" class="inline">
|
|
@ui.CSRFField(csrfToken)
|
|
for _, id := range etapeReorderIDs(etapes, index, 1) {
|
|
<input type="hidden" name="etape_id" value={ id.String() }/>
|
|
}
|
|
<button type="submit" class="ui-button ui-button-soft-neutral-md px-2" aria-label={ "Move etape later: " + etape.Title }>Down</button>
|
|
</form>
|
|
}
|
|
</div>
|
|
}
|
|
<button
|
|
type="button"
|
|
class="ui-button ui-button-soft-neutral-md flex-shrink-0"
|
|
hx-get={ "/tablos/" + tabloID.String() + "/etapes/new" }
|
|
hx-target="#etape-form-slot"
|
|
hx-swap="innerHTML"
|
|
>+ Etape</button>
|
|
</div>
|
|
<div id="etape-form-slot"></div>
|
|
</div>
|
|
}
|
|
|
|
templ EtapeEditFormFragment(tabloID uuid.UUID, etape sqlc.Etape, form EtapeUpdateForm, errs EtapeUpdateErrors, csrfToken string) {
|
|
<form
|
|
method="POST"
|
|
action={ templ.SafeURL("/tablos/" + tabloID.String() + "/etapes/" + etape.ID.String()) }
|
|
hx-post={ "/tablos/" + tabloID.String() + "/etapes/" + etape.ID.String() }
|
|
hx-target="#tasks-tab"
|
|
hx-swap="outerHTML"
|
|
class="rounded border border-slate-200 bg-white p-3 shadow-sm space-y-3"
|
|
>
|
|
@ui.CSRFField(csrfToken)
|
|
<div>
|
|
<input
|
|
type="text"
|
|
name="title"
|
|
value={ form.Title }
|
|
maxlength="255"
|
|
required
|
|
placeholder="Etape title"
|
|
class="block w-full rounded border border-slate-300 px-2 py-1 text-sm placeholder-slate-400 focus:border-slate-500 focus:outline-none"
|
|
/>
|
|
@FieldError(errs.Title)
|
|
</div>
|
|
<div>
|
|
<textarea
|
|
name="description"
|
|
rows="2"
|
|
placeholder="Description (optional)"
|
|
class="block w-full rounded border border-slate-300 px-2 py-1 text-sm placeholder-slate-400 focus:border-slate-500 focus:outline-none"
|
|
>{ form.Description }</textarea>
|
|
</div>
|
|
if errs.General != "" {
|
|
@FieldError(errs.General)
|
|
}
|
|
<div class="flex items-center gap-2">
|
|
@ui.Button(ui.ButtonProps{
|
|
Label: "Save",
|
|
Variant: ui.ButtonVariantDefault,
|
|
Tone: ui.ButtonToneSolid,
|
|
Size: ui.SizeMD,
|
|
Type: "submit",
|
|
})
|
|
@ui.Button(ui.ButtonProps{
|
|
Label: "Discard",
|
|
Variant: ui.ButtonVariantNeutral,
|
|
Tone: ui.ButtonToneSoft,
|
|
Size: ui.SizeMD,
|
|
Type: "button",
|
|
Attrs: templ.Attributes{
|
|
"hx-get": "/tablos/" + tabloID.String() + "/etapes/cancel-new",
|
|
"hx-target": "#etape-form-slot",
|
|
"hx-swap": "innerHTML",
|
|
},
|
|
})
|
|
</div>
|
|
</form>
|
|
}
|
|
|
|
templ EtapeDeleteConfirmFragment(tabloID uuid.UUID, etape sqlc.Etape, csrfToken string) {
|
|
<div class="rounded border border-slate-200 bg-white p-3 shadow-sm space-y-3">
|
|
<p class="text-sm font-semibold text-slate-800">Delete etape?</p>
|
|
<p class="text-sm text-slate-600">Tasks in this etape will stay in the tablo and move to Unassigned.</p>
|
|
<div class="flex items-center gap-2">
|
|
<form
|
|
method="POST"
|
|
action={ templ.SafeURL("/tablos/" + tabloID.String() + "/etapes/" + etape.ID.String() + "/delete") }
|
|
hx-post={ "/tablos/" + tabloID.String() + "/etapes/" + etape.ID.String() + "/delete" }
|
|
hx-target="#tasks-tab"
|
|
hx-swap="outerHTML"
|
|
>
|
|
@ui.CSRFField(csrfToken)
|
|
@ui.Button(ui.ButtonProps{
|
|
Label: "Delete etape",
|
|
Variant: ui.ButtonVariantDanger,
|
|
Tone: ui.ButtonToneSolid,
|
|
Size: ui.SizeMD,
|
|
Type: "submit",
|
|
})
|
|
</form>
|
|
@ui.Button(ui.ButtonProps{
|
|
Label: "Keep etape",
|
|
Variant: ui.ButtonVariantNeutral,
|
|
Tone: ui.ButtonToneSoft,
|
|
Size: ui.SizeMD,
|
|
Type: "button",
|
|
Attrs: templ.Attributes{
|
|
"hx-get": "/tablos/" + tabloID.String() + "/etapes/cancel-new",
|
|
"hx-target": "#etape-form-slot",
|
|
"hx-swap": "innerHTML",
|
|
},
|
|
})
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
templ EtapeCreateFormFragment(tabloID uuid.UUID, form EtapeCreateForm, errs EtapeCreateErrors, csrfToken string) {
|
|
<form
|
|
method="POST"
|
|
action={ templ.SafeURL("/tablos/" + tabloID.String() + "/etapes") }
|
|
hx-post={ "/tablos/" + tabloID.String() + "/etapes" }
|
|
hx-target="#tasks-tab"
|
|
hx-swap="outerHTML"
|
|
class="rounded border border-slate-200 bg-white p-3 shadow-sm space-y-3"
|
|
>
|
|
@ui.CSRFField(csrfToken)
|
|
<div>
|
|
<input
|
|
type="text"
|
|
name="title"
|
|
value={ form.Title }
|
|
maxlength="255"
|
|
required
|
|
placeholder="Etape title"
|
|
class="block w-full rounded border border-slate-300 px-2 py-1 text-sm placeholder-slate-400 focus:border-slate-500 focus:outline-none"
|
|
/>
|
|
@FieldError(errs.Title)
|
|
</div>
|
|
<div>
|
|
<textarea
|
|
name="description"
|
|
rows="2"
|
|
placeholder="Description (optional)"
|
|
class="block w-full rounded border border-slate-300 px-2 py-1 text-sm placeholder-slate-400 focus:border-slate-500 focus:outline-none"
|
|
>{ form.Description }</textarea>
|
|
</div>
|
|
if errs.General != "" {
|
|
@FieldError(errs.General)
|
|
}
|
|
<div class="flex items-center gap-2">
|
|
@ui.Button(ui.ButtonProps{
|
|
Label: "Save",
|
|
Variant: ui.ButtonVariantDefault,
|
|
Tone: ui.ButtonToneSolid,
|
|
Size: ui.SizeMD,
|
|
Type: "submit",
|
|
})
|
|
@ui.Button(ui.ButtonProps{
|
|
Label: "Discard",
|
|
Variant: ui.ButtonVariantNeutral,
|
|
Tone: ui.ButtonToneSoft,
|
|
Size: ui.SizeMD,
|
|
Type: "button",
|
|
Attrs: templ.Attributes{
|
|
"hx-get": "/tablos/" + tabloID.String() + "/etapes/cancel-new",
|
|
"hx-target": "#etape-form-slot",
|
|
"hx-swap": "innerHTML",
|
|
},
|
|
})
|
|
</div>
|
|
</form>
|
|
}
|