xtablo-source/go-backend/internal/web/views/tablos.templ

293 lines
9.1 KiB
Text

package views
import "xtablo-backend/internal/web/ui"
templ TablosPageContent(vm TablosPageViewModel) {
<div class="px-4 pt-8 pb-6">
<div class="flex flex-col md:flex-row md:items-center md:justify-between mb-8 gap-4">
<h1 class="text-2xl font-bold text-gray-900 dark:text-gray-100">Mes Projets</h1>
@ui.Button(ui.ButtonProps{
Label: "Nouveau projet",
Variant: ui.ButtonVariantDefault,
Size: ui.SizeMD,
Type: "button",
Icon: "plus",
Attrs: templ.Attributes{
"hx-get": vm.CreateModalHref(),
"hx-target": "#app-main-content",
"hx-swap": "outerHTML",
"hx-push-url": "true",
},
})
</div>
<div class="flex items-center gap-6 mb-6 border-b border-[#EAECF0] dark:border-gray-700">
<a
href={ templ.SafeURL(vm.ViewHref("grid")) }
hx-get={ vm.ViewHref("grid") }
hx-target="#app-main-content"
hx-swap="outerHTML"
hx-push-url="true"
class={ gridToggleClass(vm.IsGridView()) }
>
<span class="w-5 h-5">
@ActionIcon("grid3x3")
</span>
<span class="font-medium">Vue en grille</span>
</a>
<a
href={ templ.SafeURL(vm.ViewHref("list")) }
hx-get={ vm.ViewHref("list") }
hx-target="#app-main-content"
hx-swap="outerHTML"
hx-push-url="true"
class={ listToggleClass(vm.IsGridView()) }
>
<span class="w-5 h-5">
@ActionIcon("list")
</span>
<span class="font-medium">Vue en liste</span>
</a>
</div>
<div class="flex flex-col md:flex-row gap-4 mb-6">
<form
class="relative md:w-[350px]"
hx-get={ vm.SearchHref() }
hx-target="#app-main-content"
hx-swap="outerHTML"
hx-push-url="true"
hx-trigger="input changed delay:300ms from:input[name='q']"
>
<input type="hidden" name="view" value={ vm.View }/>
<input type="hidden" name="status" value={ vm.Status }/>
<span class="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5 pointer-events-none">
@ActionIcon("search")
</span>
<input
name="q"
value={ vm.Query }
placeholder="Rechercher..."
class="w-full pl-10 pr-4 py-3 border border-[#EAECF0] dark:border-gray-700 rounded-[8px] focus:outline-none focus:ring-2 focus:ring-purple-500 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 placeholder-gray-400"
type="text"
/>
</form>
<div class="flex items-center gap-2 flex-wrap">
@StatusPill(vm, "all", "Tous")
@StatusPill(vm, "todo", "Pas commencé")
@StatusPill(vm, "in_progress", "En cours")
@StatusPill(vm, "done", "Terminé")
</div>
</div>
if vm.HasTablos() {
if vm.IsGridView() {
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 sm:gap-6">
for _, tablo := range vm.Tablos {
@TabloGridCard(tablo)
}
</div>
} else {
<div class="bg-white dark:bg-gray-800 rounded-xl border border-[#EAECF0] dark:border-gray-700 overflow-x-auto -mx-4 sm:mx-0">
@ui.Table(ui.TableProps{
Head: TabloListHead(),
Body: TabloListBody(vm.Tablos),
})
</div>
}
} else {
@ui.EmptyState(ui.EmptyStateProps{
Title: "Aucun projet trouvé",
Description: "Créez votre premier projet",
Icon: ui.UIIcon("grid3x3"),
Action: ui.Button(ui.ButtonProps{
Label: "Nouveau projet",
Variant: ui.ButtonVariantDefault,
Size: ui.SizeMD,
Type: "button",
Icon: "plus",
Attrs: templ.Attributes{
"hx-get": vm.CreateModalHref(),
"hx-target": "#app-main-content",
"hx-swap": "outerHTML",
"hx-push-url": "true",
},
}),
})
}
if vm.ModalOpen {
@CreateTabloModal(vm)
}
</div>
}
templ StatusPill(vm TablosPageViewModel, status string, label string) {
<a
href={ templ.SafeURL(vm.StatusHref(status)) }
hx-get={ vm.StatusHref(status) }
hx-target="#app-main-content"
hx-swap="outerHTML"
hx-push-url="true"
class={ statusPillClass(vm.Status == status) }
>
if status == "all" {
<span class="w-4 h-4">
@ActionIcon("filter")
</span>
}
{ label }
</a>
}
templ BorderlessDeleteButton(deleteRequestURL string) {
@ui.IconButton(ui.IconButtonProps{
Label: "Supprimer le projet",
Icon: "trash",
Variant: ui.IconButtonVariantDangerGhost,
Type: "button",
Attrs: templ.Attributes{
"hx-delete": deleteRequestURL,
"hx-target": "#app-main-content",
"hx-swap": "outerHTML",
"hx-confirm": "Supprimer ce projet ?",
},
})
}
templ TabloGridCard(tablo TabloCardView) {
@TabloGridCardWithAttrs(tablo, nil)
}
templ TabloGridCardWithAttrs(tablo TabloCardView, attrs templ.Attributes) {
<article class="project-card" { attrs... }>
<div class="project-card-top">
@ui.Badge(ui.BadgeProps{
Label: tablo.StatusLabel,
Variant: badgeVariantForTone(tablo.StatusTone),
})
@BorderlessDeleteButton(tablo.DeleteRequestURL)
</div>
<div class="project-card-title-row">
<div class={ "project-avatar " + projectAccentClass(tablo.Accent) }>
<span>{ tablo.Initial }</span>
</div>
<h4>{ tablo.Name }</h4>
</div>
<div class="project-date-row">
@ActionIcon("calendar")
<span>{ tablo.CardDateLabel }</span>
</div>
<div class="project-progress">
<div class="project-progress-label">
<span>Progression:</span>
<strong>{ tablo.ProgressLabel }</strong>
</div>
<div class="project-progress-track">
<div class={ "project-progress-bar " + projectAccentClass(tablo.Accent) } style={ progressInlineStyle(tablo.Progress) }></div>
</div>
</div>
</article>
}
templ TabloListRow(tablo TabloCardView) {
<tr class="border-t border-[#EAECF0] dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors cursor-pointer">
<td class="px-6 py-4">
<div class="flex items-center gap-3">
<div class={ "w-8 h-8 rounded-lg flex items-center justify-center shrink-0 overflow-hidden [&>svg]:w-4 [&>svg]:h-4 " + tablo.IconBgClass + " " + tablo.IconFgClass }>
@ActionIcon(tablo.IconKind)
</div>
<span class="font-medium text-gray-900 dark:text-gray-100 truncate">{ tablo.Name }</span>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
@ui.Badge(ui.BadgeProps{
Label: tablo.StatusLabel,
Variant: badgeVariantForTone(tablo.StatusTone),
})
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-600 dark:text-gray-400">
<div class="flex items-center gap-1.5 [&>svg]:w-4 [&>svg]:h-4 [&>svg]:shrink-0">
@ActionIcon("calendar")
{ tablo.CreatedAtLabel }
</div>
</td>
<td class="px-6 py-4">
<div class="flex items-center gap-3">
<div class="flex-1 bg-gray-200 dark:bg-gray-700 rounded-full h-2 min-w-[80px]">
<div class="bg-green-500 h-2 rounded-full transition-all" style={ progressInlineStyle(tablo.Progress) }></div>
</div>
<span class="text-sm font-semibold text-gray-900 dark:text-gray-100 w-8 text-right">{ tablo.ProgressLabel }</span>
</div>
</td>
<td class="px-6 py-4 text-right">
@BorderlessDeleteButton(tablo.DeleteRequestURL)
</td>
</tr>
}
templ CreateTabloModal(vm TablosPageViewModel) {
@ui.Modal(ui.ModalProps{
Title: "Nouveau projet",
Body: CreateTabloModalBody(vm),
})
}
templ TabloListHead() {
<tr class="bg-gray-50 dark:bg-gray-800/80 border-b border-[#EAECF0] dark:border-gray-700">
<th class="px-6 py-3 text-left text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase tracking-wider">Projet</th>
<th class="px-6 py-3 text-left text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase tracking-wider">Statut</th>
<th class="px-6 py-3 text-left text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase tracking-wider">Créé le</th>
<th class="px-6 py-3 text-left text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase tracking-wider">Progression</th>
<th class="px-6 py-3 w-12"></th>
</tr>
}
templ TabloListBody(tablos []TabloCardView) {
for _, tablo := range tablos {
@TabloListRow(tablo)
}
}
templ CreateTabloModalBody(vm TablosPageViewModel) {
<form
hx-post="/tablos"
hx-target="#app-main-content"
hx-swap="outerHTML"
class="flex flex-col gap-4"
>
<input type="hidden" name="view" value={ vm.View }/>
<input type="hidden" name="status" value={ vm.Status }/>
<input type="hidden" name="q" value={ vm.Query }/>
<input type="hidden" name="modal" value="create"/>
if vm.ErrorMessage != "" {
<div class="mb-1 rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700">{ vm.ErrorMessage }</div>
}
@ui.FormField(ui.FormFieldProps{
Label: "Nom du projet",
For: "tablo-name",
Field: ui.Input(ui.InputProps{
ID: "tablo-name",
Name: "name",
Value: vm.FormName,
Placeholder: "Nom du projet",
Type: "text",
}),
Error: vm.ErrorMessage,
})
<div class="flex items-center justify-end gap-3">
<a
href={ templ.SafeURL(vm.CloseModalHref()) }
hx-get={ vm.CloseModalHref() }
hx-target="#app-main-content"
hx-swap="outerHTML"
hx-push-url="true"
class="ui-button ui-button-solid ui-button-neutral ui-button-md"
>
Annuler
</a>
@ui.Button(ui.ButtonProps{
Label: "Créer le projet",
Variant: ui.ButtonVariantDefault,
Size: ui.SizeMD,
Type: "submit",
})
</div>
</form>
}