Add spacing, update catalog, and make several improvements to the tablo:
add update
This commit is contained in:
parent
c89f526780
commit
b84eff7887
39 changed files with 1666 additions and 374 deletions
|
|
@ -8,6 +8,6 @@
|
|||
<link rel="stylesheet" href="../../go-backend/static/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<main class="catalog-page"><nav class="catalog-nav" aria-label="Catalog navigation"><a href="./index.html" class="catalog-home-link">Catalog</a><div class="catalog-nav-links"><a href="./tokens.html" class="catalog-nav-link">Tokens</a><a href="./buttons.html" class="catalog-nav-link">Buttons</a><a href="./badges.html" class="catalog-nav-link is-active">Badges</a><a href="./icon-buttons.html" class="catalog-nav-link">Icon Buttons</a><a href="./inputs.html" class="catalog-nav-link">Inputs</a><a href="./form-fields.html" class="catalog-nav-link">Form Fields</a><a href="./modals.html" class="catalog-nav-link">Modals</a><a href="./tables.html" class="catalog-nav-link">Tables</a><a href="./empty-states.html" class="catalog-nav-link">Empty States</a><a href="./cards.html" class="catalog-nav-link">Cards</a></div></nav><header class="catalog-page-header"><p class="catalog-eyebrow">Design System</p><h1>Badges</h1><p>Semantic status labels for todo, in-progress, success, and destructive states.</p></header><div class="catalog-example-list"><section class="catalog-example"><div class="catalog-example-copy"><h2>Status set</h2><p>The four semantic badge tones used across the app.</p></div><div class="catalog-example-preview"><div class="catalog-inline"><span class="ui-badge ui-badge-info">À faire</span></div><div class="catalog-inline"><span class="ui-badge ui-badge-warning">En cours</span></div><div class="catalog-inline"><span class="ui-badge ui-badge-success">Terminé</span></div><div class="catalog-inline"><span class="ui-badge ui-badge-danger">Erreur</span></div></div><pre class="catalog-example-snippet"><code>@ui.Badge(ui.BadgeProps{Label: "En cours", Variant: ui.BadgeVariantWarning})</code></pre></section></div></main>
|
||||
<main class="catalog-page"><nav class="catalog-nav" aria-label="Catalog navigation"><a href="./index.html" class="catalog-home-link">Catalog</a><div class="catalog-nav-links"><a href="./tokens.html" class="catalog-nav-link">Tokens</a><a href="./buttons.html" class="catalog-nav-link">Buttons</a><a href="./badges.html" class="catalog-nav-link is-active">Badges</a><a href="./icon-buttons.html" class="catalog-nav-link">Icon Buttons</a><a href="./inputs.html" class="catalog-nav-link">Inputs</a><a href="./form-fields.html" class="catalog-nav-link">Form Fields</a><a href="./modals.html" class="catalog-nav-link">Modals</a><a href="./spacing.html" class="catalog-nav-link">Spacing</a><a href="./tables.html" class="catalog-nav-link">Tables</a><a href="./empty-states.html" class="catalog-nav-link">Empty States</a><a href="./cards.html" class="catalog-nav-link">Cards</a></div></nav><header class="catalog-page-header"><p class="catalog-eyebrow">Design System</p><h1>Badges</h1><p>Semantic status labels for todo, in-progress, success, and destructive states.</p></header><div class="catalog-example-list"><section class="catalog-example"><div class="catalog-example-copy"><h2>Status set</h2><p>The four semantic badge tones used across the app.</p></div><div class="catalog-example-preview"><div class="catalog-inline"><span class="ui-badge ui-badge-info">À faire</span></div><div class="catalog-inline"><span class="ui-badge ui-badge-warning">En cours</span></div><div class="catalog-inline"><span class="ui-badge ui-badge-success">Terminé</span></div><div class="catalog-inline"><span class="ui-badge ui-badge-danger">Erreur</span></div></div><pre class="catalog-example-snippet"><code>@ui.Badge(ui.BadgeProps{Label: "En cours", Variant: ui.BadgeVariantWarning})</code></pre></section></div></main>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<link rel="stylesheet" href="../../go-backend/static/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<main class="catalog-page"><nav class="catalog-nav" aria-label="Catalog navigation"><a href="./index.html" class="catalog-home-link">Catalog</a><div class="catalog-nav-links"><a href="./tokens.html" class="catalog-nav-link">Tokens</a><a href="./buttons.html" class="catalog-nav-link is-active">Buttons</a><a href="./badges.html" class="catalog-nav-link">Badges</a><a href="./icon-buttons.html" class="catalog-nav-link">Icon Buttons</a><a href="./inputs.html" class="catalog-nav-link">Inputs</a><a href="./form-fields.html" class="catalog-nav-link">Form Fields</a><a href="./modals.html" class="catalog-nav-link">Modals</a><a href="./tables.html" class="catalog-nav-link">Tables</a><a href="./empty-states.html" class="catalog-nav-link">Empty States</a><a href="./cards.html" class="catalog-nav-link">Cards</a></div></nav><header class="catalog-page-header"><p class="catalog-eyebrow">Design System</p><h1>Buttons</h1><p>Primary, secondary, ghost, and destructive actions built from shared templ primitives.</p></header><div class="catalog-example-list"><section class="catalog-example"><div class="catalog-example-copy"><h2>Default solid action</h2><p>Used for the main action in a page section or modal footer.</p></div><div class="catalog-example-preview"><button type="button" class="ui-button ui-button-solid ui-button-default ui-button-md">Nouveau projet</button></div><pre class="catalog-example-snippet"><code>@ui.Button(ui.ButtonProps{
|
||||
<main class="catalog-page"><nav class="catalog-nav" aria-label="Catalog navigation"><a href="./index.html" class="catalog-home-link">Catalog</a><div class="catalog-nav-links"><a href="./tokens.html" class="catalog-nav-link">Tokens</a><a href="./buttons.html" class="catalog-nav-link is-active">Buttons</a><a href="./badges.html" class="catalog-nav-link">Badges</a><a href="./icon-buttons.html" class="catalog-nav-link">Icon Buttons</a><a href="./inputs.html" class="catalog-nav-link">Inputs</a><a href="./form-fields.html" class="catalog-nav-link">Form Fields</a><a href="./modals.html" class="catalog-nav-link">Modals</a><a href="./spacing.html" class="catalog-nav-link">Spacing</a><a href="./tables.html" class="catalog-nav-link">Tables</a><a href="./empty-states.html" class="catalog-nav-link">Empty States</a><a href="./cards.html" class="catalog-nav-link">Cards</a></div></nav><header class="catalog-page-header"><p class="catalog-eyebrow">Design System</p><h1>Buttons</h1><p>Primary, secondary, ghost, and destructive actions built from shared templ primitives.</p></header><div class="catalog-example-list"><section class="catalog-example"><div class="catalog-example-copy"><h2>Default solid action</h2><p>Used for the main action in a page section or modal footer.</p></div><div class="catalog-example-preview"><button type="button" class="ui-button ui-button-solid ui-button-default ui-button-md">Nouveau projet</button></div><pre class="catalog-example-snippet"><code>@ui.Button(ui.ButtonProps{
|
||||
Label: "Nouveau projet",
|
||||
Variant: ui.ButtonVariantDefault,
|
||||
Size: ui.SizeMD,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<link rel="stylesheet" href="../../go-backend/static/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<main class="catalog-page"><nav class="catalog-nav" aria-label="Catalog navigation"><a href="./index.html" class="catalog-home-link">Catalog</a><div class="catalog-nav-links"><a href="./tokens.html" class="catalog-nav-link">Tokens</a><a href="./buttons.html" class="catalog-nav-link">Buttons</a><a href="./badges.html" class="catalog-nav-link">Badges</a><a href="./icon-buttons.html" class="catalog-nav-link">Icon Buttons</a><a href="./inputs.html" class="catalog-nav-link">Inputs</a><a href="./form-fields.html" class="catalog-nav-link">Form Fields</a><a href="./modals.html" class="catalog-nav-link">Modals</a><a href="./tables.html" class="catalog-nav-link">Tables</a><a href="./empty-states.html" class="catalog-nav-link">Empty States</a><a href="./cards.html" class="catalog-nav-link is-active">Cards</a></div></nav><header class="catalog-page-header"><p class="catalog-eyebrow">Design System</p><h1>Cards</h1><p>Reusable bordered surfaces with optional header, body, and footer regions.</p></header><div class="catalog-example-list"><section class="catalog-example"><div class="catalog-example-copy"><h2>Surface card</h2><p>Generic elevated surface with optional header and footer.</p></div><div class="catalog-example-preview"><section class="ui-card"><div class="ui-card-header">Header</div><div class="ui-card-body">Body</div><div class="ui-card-footer">Footer</div></section></div><pre class="catalog-example-snippet"><code>@ui.Card(ui.CardProps{
|
||||
<main class="catalog-page"><nav class="catalog-nav" aria-label="Catalog navigation"><a href="./index.html" class="catalog-home-link">Catalog</a><div class="catalog-nav-links"><a href="./tokens.html" class="catalog-nav-link">Tokens</a><a href="./buttons.html" class="catalog-nav-link">Buttons</a><a href="./badges.html" class="catalog-nav-link">Badges</a><a href="./icon-buttons.html" class="catalog-nav-link">Icon Buttons</a><a href="./inputs.html" class="catalog-nav-link">Inputs</a><a href="./form-fields.html" class="catalog-nav-link">Form Fields</a><a href="./modals.html" class="catalog-nav-link">Modals</a><a href="./spacing.html" class="catalog-nav-link">Spacing</a><a href="./tables.html" class="catalog-nav-link">Tables</a><a href="./empty-states.html" class="catalog-nav-link">Empty States</a><a href="./cards.html" class="catalog-nav-link is-active">Cards</a></div></nav><header class="catalog-page-header"><p class="catalog-eyebrow">Design System</p><h1>Cards</h1><p>Reusable bordered surfaces with optional header, body, and footer regions.</p></header><div class="catalog-example-list"><section class="catalog-example"><div class="catalog-example-copy"><h2>Surface card</h2><p>Generic elevated surface with optional header and footer.</p></div><div class="catalog-example-preview"><section class="ui-card"><div class="ui-card-header">Header</div><div class="ui-card-body">Body</div><div class="ui-card-footer">Footer</div></section></div><pre class="catalog-example-snippet"><code>@ui.Card(ui.CardProps{
|
||||
Header: textComponent("Header"),
|
||||
Body: textComponent("Body"),
|
||||
Footer: textComponent("Footer"),
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<link rel="stylesheet" href="../../go-backend/static/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<main class="catalog-page"><nav class="catalog-nav" aria-label="Catalog navigation"><a href="./index.html" class="catalog-home-link">Catalog</a><div class="catalog-nav-links"><a href="./tokens.html" class="catalog-nav-link">Tokens</a><a href="./buttons.html" class="catalog-nav-link">Buttons</a><a href="./badges.html" class="catalog-nav-link">Badges</a><a href="./icon-buttons.html" class="catalog-nav-link">Icon Buttons</a><a href="./inputs.html" class="catalog-nav-link">Inputs</a><a href="./form-fields.html" class="catalog-nav-link">Form Fields</a><a href="./modals.html" class="catalog-nav-link">Modals</a><a href="./tables.html" class="catalog-nav-link">Tables</a><a href="./empty-states.html" class="catalog-nav-link is-active">Empty States</a><a href="./cards.html" class="catalog-nav-link">Cards</a></div></nav><header class="catalog-page-header"><p class="catalog-eyebrow">Design System</p><h1>Empty States</h1><p>Centered fallback messaging with optional icon and action.</p></header><div class="catalog-example-list"><section class="catalog-example"><div class="catalog-example-copy"><h2>Centered empty state</h2><p>Used when a list has no rows yet and the next action should stay obvious.</p></div><div class="catalog-example-preview"><section class="ui-empty-state"><div class="ui-empty-state-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect width="18" height="18" x="3" y="3" rx="2"></rect> <path d="M3 9h18"></path> <path d="M3 15h18"></path> <path d="M9 3v18"></path> <path d="M15 3v18"></path></svg></div><h3 class="ui-empty-state-title">Aucun projet trouvé</h3><p class="ui-empty-state-description">Créez votre premier projet</p><div class="ui-empty-state-action"><button type="button" class="ui-button ui-button-solid ui-button-default ui-button-md"><span class="ui-button-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12h14"></path> <path d="M12 5v14"></path></svg></span> Nouveau projet</button></div></section></div><pre class="catalog-example-snippet"><code>@ui.EmptyState(ui.EmptyStateProps{
|
||||
<main class="catalog-page"><nav class="catalog-nav" aria-label="Catalog navigation"><a href="./index.html" class="catalog-home-link">Catalog</a><div class="catalog-nav-links"><a href="./tokens.html" class="catalog-nav-link">Tokens</a><a href="./buttons.html" class="catalog-nav-link">Buttons</a><a href="./badges.html" class="catalog-nav-link">Badges</a><a href="./icon-buttons.html" class="catalog-nav-link">Icon Buttons</a><a href="./inputs.html" class="catalog-nav-link">Inputs</a><a href="./form-fields.html" class="catalog-nav-link">Form Fields</a><a href="./modals.html" class="catalog-nav-link">Modals</a><a href="./spacing.html" class="catalog-nav-link">Spacing</a><a href="./tables.html" class="catalog-nav-link">Tables</a><a href="./empty-states.html" class="catalog-nav-link is-active">Empty States</a><a href="./cards.html" class="catalog-nav-link">Cards</a></div></nav><header class="catalog-page-header"><p class="catalog-eyebrow">Design System</p><h1>Empty States</h1><p>Centered fallback messaging with optional icon and action.</p></header><div class="catalog-example-list"><section class="catalog-example"><div class="catalog-example-copy"><h2>Centered empty state</h2><p>Used when a list has no rows yet and the next action should stay obvious.</p></div><div class="catalog-example-preview"><section class="ui-empty-state"><div class="ui-empty-state-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect width="18" height="18" x="3" y="3" rx="2"></rect> <path d="M3 9h18"></path> <path d="M3 15h18"></path> <path d="M9 3v18"></path> <path d="M15 3v18"></path></svg></div><h3 class="ui-empty-state-title">Aucun projet trouvé</h3><p class="ui-empty-state-description">Créez votre premier projet</p><div class="ui-empty-state-action"><button type="button" class="ui-button ui-button-solid ui-button-default ui-button-md"><span class="ui-button-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12h14"></path> <path d="M12 5v14"></path></svg></span> Nouveau projet</button></div></section></div><pre class="catalog-example-snippet"><code>@ui.EmptyState(ui.EmptyStateProps{
|
||||
Title: "Aucun projet trouvé",
|
||||
Description: "Créez votre premier projet",
|
||||
Icon: ui.UIIcon("grid3x3"),
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<link rel="stylesheet" href="../../go-backend/static/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<main class="catalog-page"><nav class="catalog-nav" aria-label="Catalog navigation"><a href="./index.html" class="catalog-home-link">Catalog</a><div class="catalog-nav-links"><a href="./tokens.html" class="catalog-nav-link">Tokens</a><a href="./buttons.html" class="catalog-nav-link">Buttons</a><a href="./badges.html" class="catalog-nav-link">Badges</a><a href="./icon-buttons.html" class="catalog-nav-link">Icon Buttons</a><a href="./inputs.html" class="catalog-nav-link">Inputs</a><a href="./form-fields.html" class="catalog-nav-link is-active">Form Fields</a><a href="./modals.html" class="catalog-nav-link">Modals</a><a href="./tables.html" class="catalog-nav-link">Tables</a><a href="./empty-states.html" class="catalog-nav-link">Empty States</a><a href="./cards.html" class="catalog-nav-link">Cards</a></div></nav><header class="catalog-page-header"><p class="catalog-eyebrow">Design System</p><h1>Form Fields</h1><p>Labeled controls with optional hint and error messaging.</p></header><div class="catalog-example-list"><section class="catalog-example"><div class="catalog-example-copy"><h2>Field with validation</h2><p>Wraps a control with label and inline error feedback.</p></div><div class="catalog-example-preview"><div class="ui-form-field"><label for="catalog-name" class="ui-form-label">Nom</label> <input id="catalog-name" type="text" name="name" value="" placeholder="Nom du projet" class="ui-input"><p class="ui-form-error">Le nom est requis</p></div></div><pre class="catalog-example-snippet"><code>@ui.FormField(ui.FormFieldProps{
|
||||
<main class="catalog-page"><nav class="catalog-nav" aria-label="Catalog navigation"><a href="./index.html" class="catalog-home-link">Catalog</a><div class="catalog-nav-links"><a href="./tokens.html" class="catalog-nav-link">Tokens</a><a href="./buttons.html" class="catalog-nav-link">Buttons</a><a href="./badges.html" class="catalog-nav-link">Badges</a><a href="./icon-buttons.html" class="catalog-nav-link">Icon Buttons</a><a href="./inputs.html" class="catalog-nav-link">Inputs</a><a href="./form-fields.html" class="catalog-nav-link is-active">Form Fields</a><a href="./modals.html" class="catalog-nav-link">Modals</a><a href="./spacing.html" class="catalog-nav-link">Spacing</a><a href="./tables.html" class="catalog-nav-link">Tables</a><a href="./empty-states.html" class="catalog-nav-link">Empty States</a><a href="./cards.html" class="catalog-nav-link">Cards</a></div></nav><header class="catalog-page-header"><p class="catalog-eyebrow">Design System</p><h1>Form Fields</h1><p>Labeled controls with optional hint and error messaging.</p></header><div class="catalog-example-list"><section class="catalog-example"><div class="catalog-example-copy"><h2>Field with validation</h2><p>Wraps a control with label and inline error feedback.</p></div><div class="catalog-example-preview"><div class="ui-form-field"><label for="catalog-name" class="ui-form-label">Nom</label> <input id="catalog-name" type="text" name="name" value="" placeholder="Nom du projet" class="ui-input"><p class="ui-form-error">Le nom est requis</p></div></div><pre class="catalog-example-snippet"><code>@ui.FormField(ui.FormFieldProps{
|
||||
Label: "Nom",
|
||||
For: "catalog-name",
|
||||
Field: ui.Input(ui.InputProps{
|
||||
|
|
|
|||
|
|
@ -8,10 +8,17 @@
|
|||
<link rel="stylesheet" href="../../go-backend/static/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<main class="catalog-page"><nav class="catalog-nav" aria-label="Catalog navigation"><a href="./index.html" class="catalog-home-link">Catalog</a><div class="catalog-nav-links"><a href="./tokens.html" class="catalog-nav-link">Tokens</a><a href="./buttons.html" class="catalog-nav-link">Buttons</a><a href="./badges.html" class="catalog-nav-link">Badges</a><a href="./icon-buttons.html" class="catalog-nav-link is-active">Icon Buttons</a><a href="./inputs.html" class="catalog-nav-link">Inputs</a><a href="./form-fields.html" class="catalog-nav-link">Form Fields</a><a href="./modals.html" class="catalog-nav-link">Modals</a><a href="./tables.html" class="catalog-nav-link">Tables</a><a href="./empty-states.html" class="catalog-nav-link">Empty States</a><a href="./cards.html" class="catalog-nav-link">Cards</a></div></nav><header class="catalog-page-header"><p class="catalog-eyebrow">Design System</p><h1>Icon Buttons</h1><p>Compact icon-only actions for destructive and neutral controls.</p></header><div class="catalog-example-list"><section class="catalog-example"><div class="catalog-example-copy"><h2>Borderless destructive action</h2><p>Used for delete controls inside project cards and list rows.</p></div><div class="catalog-example-preview"><button type="button" class="borderless-icon-button" aria-label="Supprimer le projet"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-trash2 w-4 h-4" aria-hidden="true"><path d="M3 6h18"></path> <path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"></path> <path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"></path> <line x1="10" x2="10" y1="11" y2="17"></line> <line x1="14" x2="14" y1="11" y2="17"></line></svg></button></div><pre class="catalog-example-snippet"><code>@ui.IconButton(ui.IconButtonProps{
|
||||
<main class="catalog-page"><nav class="catalog-nav" aria-label="Catalog navigation"><a href="./index.html" class="catalog-home-link">Catalog</a><div class="catalog-nav-links"><a href="./tokens.html" class="catalog-nav-link">Tokens</a><a href="./buttons.html" class="catalog-nav-link">Buttons</a><a href="./badges.html" class="catalog-nav-link">Badges</a><a href="./icon-buttons.html" class="catalog-nav-link is-active">Icon Buttons</a><a href="./inputs.html" class="catalog-nav-link">Inputs</a><a href="./form-fields.html" class="catalog-nav-link">Form Fields</a><a href="./modals.html" class="catalog-nav-link">Modals</a><a href="./spacing.html" class="catalog-nav-link">Spacing</a><a href="./tables.html" class="catalog-nav-link">Tables</a><a href="./empty-states.html" class="catalog-nav-link">Empty States</a><a href="./cards.html" class="catalog-nav-link">Cards</a></div></nav><header class="catalog-page-header"><p class="catalog-eyebrow">Design System</p><h1>Icon Buttons</h1><p>Compact icon-only actions for destructive and neutral controls.</p></header><div class="catalog-example-list"><section class="catalog-example"><div class="catalog-example-copy"><h2>Borderless destructive action</h2><p>Used for delete controls inside project cards and list rows.</p></div><div class="catalog-example-preview"><button type="button" class="borderless-icon-button ui-icon-button-ghost ui-icon-button-danger" aria-label="Supprimer le projet"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-trash2 w-4 h-4" aria-hidden="true"><path d="M3 6h18"></path> <path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"></path> <path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"></path> <line x1="10" x2="10" y1="11" y2="17"></line> <line x1="14" x2="14" y1="11" y2="17"></line></svg></button></div><pre class="catalog-example-snippet"><code>@ui.IconButton(ui.IconButtonProps{
|
||||
Label: "Supprimer le projet",
|
||||
Icon: "trash",
|
||||
Variant: ui.IconButtonVariantDangerGhost,
|
||||
Variant: ui.IconButtonVariantDanger,
|
||||
Tone: ui.IconButtonToneGhost,
|
||||
Type: "button",
|
||||
})</code></pre></section><section class="catalog-example"><div class="catalog-example-copy"><h2>Borderless neutral action</h2><p>Used for lightweight edit or details actions inside cards and list rows.</p></div><div class="catalog-example-preview"><button type="button" class="borderless-icon-button ui-icon-button-ghost ui-icon-button-neutral" aria-label="Modifier le projet"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 20h9"></path> <path d="M16.5 3.5a2.12 2.12 0 1 1 3 3L7 19l-4 1 1-4Z"></path></svg></button></div><pre class="catalog-example-snippet"><code>@ui.IconButton(ui.IconButtonProps{
|
||||
Label: "Modifier le projet",
|
||||
Icon: "pencil",
|
||||
Variant: ui.IconButtonVariantNeutral,
|
||||
Tone: ui.IconButtonToneGhost,
|
||||
Type: "button",
|
||||
})</code></pre></section></div></main>
|
||||
</body>
|
||||
|
|
|
|||
|
|
@ -8,6 +8,6 @@
|
|||
<link rel="stylesheet" href="../../go-backend/static/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<main class="catalog-page"><header class="catalog-page-header"><p class="catalog-eyebrow">Design System</p><h1>Component Catalog</h1><p>Static documentation generated from the same templ primitives used by the Go application.</p></header><div class="catalog-page-list"><a href="./tokens.html" class="catalog-page-link-card"><h2>Tokens</h2><p>Semantic colors and status roles used by the Go design system.</p><p class="catalog-page-link">/tokens.html</p></a><a href="./buttons.html" class="catalog-page-link-card"><h2>Buttons</h2><p>Primary, secondary, ghost, and destructive actions built from shared templ primitives.</p><p class="catalog-page-link">/buttons.html</p></a><a href="./badges.html" class="catalog-page-link-card"><h2>Badges</h2><p>Semantic status labels for todo, in-progress, success, and destructive states.</p><p class="catalog-page-link">/badges.html</p></a><a href="./icon-buttons.html" class="catalog-page-link-card"><h2>Icon Buttons</h2><p>Compact icon-only actions for destructive and neutral controls.</p><p class="catalog-page-link">/icon-buttons.html</p></a><a href="./inputs.html" class="catalog-page-link-card"><h2>Inputs</h2><p>Shared single-line and multiline text controls.</p><p class="catalog-page-link">/inputs.html</p></a><a href="./form-fields.html" class="catalog-page-link-card"><h2>Form Fields</h2><p>Labeled controls with optional hint and error messaging.</p><p class="catalog-page-link">/form-fields.html</p></a><a href="./modals.html" class="catalog-page-link-card"><h2>Modals</h2><p>Shared modal shell for focused create, edit, and confirm flows.</p><p class="catalog-page-link">/modals.html</p></a><a href="./tables.html" class="catalog-page-link-card"><h2>Tables</h2><p>Shared table shell for server-rendered list views.</p><p class="catalog-page-link">/tables.html</p></a><a href="./empty-states.html" class="catalog-page-link-card"><h2>Empty States</h2><p>Centered fallback messaging with optional icon and action.</p><p class="catalog-page-link">/empty-states.html</p></a><a href="./cards.html" class="catalog-page-link-card"><h2>Cards</h2><p>Reusable bordered surfaces with optional header, body, and footer regions.</p><p class="catalog-page-link">/cards.html</p></a></div></main>
|
||||
<main class="catalog-page"><header class="catalog-page-header"><p class="catalog-eyebrow">Design System</p><h1>Component Catalog</h1><p>Static documentation generated from the same templ primitives used by the Go application.</p></header><div class="catalog-page-list"><a href="./tokens.html" class="catalog-page-link-card"><h2>Tokens</h2><p>Semantic colors and status roles used by the Go design system.</p><p class="catalog-page-link">/tokens.html</p></a><a href="./buttons.html" class="catalog-page-link-card"><h2>Buttons</h2><p>Primary, secondary, ghost, and destructive actions built from shared templ primitives.</p><p class="catalog-page-link">/buttons.html</p></a><a href="./badges.html" class="catalog-page-link-card"><h2>Badges</h2><p>Semantic status labels for todo, in-progress, success, and destructive states.</p><p class="catalog-page-link">/badges.html</p></a><a href="./icon-buttons.html" class="catalog-page-link-card"><h2>Icon Buttons</h2><p>Compact icon-only actions for destructive and neutral controls.</p><p class="catalog-page-link">/icon-buttons.html</p></a><a href="./inputs.html" class="catalog-page-link-card"><h2>Inputs</h2><p>Shared single-line and multiline text controls.</p><p class="catalog-page-link">/inputs.html</p></a><a href="./form-fields.html" class="catalog-page-link-card"><h2>Form Fields</h2><p>Labeled controls with optional hint and error messaging.</p><p class="catalog-page-link">/form-fields.html</p></a><a href="./modals.html" class="catalog-page-link-card"><h2>Modals</h2><p>Shared modal shell for focused create, edit, and confirm flows.</p><p class="catalog-page-link">/modals.html</p></a><a href="./spacing.html" class="catalog-page-link-card"><h2>Spacing</h2><p>Fixed horizontal and vertical spacer primitives for composing gaps between UI components.</p><p class="catalog-page-link">/spacing.html</p></a><a href="./tables.html" class="catalog-page-link-card"><h2>Tables</h2><p>Shared table shell for server-rendered list views.</p><p class="catalog-page-link">/tables.html</p></a><a href="./empty-states.html" class="catalog-page-link-card"><h2>Empty States</h2><p>Centered fallback messaging with optional icon and action.</p><p class="catalog-page-link">/empty-states.html</p></a><a href="./cards.html" class="catalog-page-link-card"><h2>Cards</h2><p>Reusable bordered surfaces with optional header, body, and footer regions.</p><p class="catalog-page-link">/cards.html</p></a></div></main>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<link rel="stylesheet" href="../../go-backend/static/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<main class="catalog-page"><nav class="catalog-nav" aria-label="Catalog navigation"><a href="./index.html" class="catalog-home-link">Catalog</a><div class="catalog-nav-links"><a href="./tokens.html" class="catalog-nav-link">Tokens</a><a href="./buttons.html" class="catalog-nav-link">Buttons</a><a href="./badges.html" class="catalog-nav-link">Badges</a><a href="./icon-buttons.html" class="catalog-nav-link">Icon Buttons</a><a href="./inputs.html" class="catalog-nav-link is-active">Inputs</a><a href="./form-fields.html" class="catalog-nav-link">Form Fields</a><a href="./modals.html" class="catalog-nav-link">Modals</a><a href="./tables.html" class="catalog-nav-link">Tables</a><a href="./empty-states.html" class="catalog-nav-link">Empty States</a><a href="./cards.html" class="catalog-nav-link">Cards</a></div></nav><header class="catalog-page-header"><p class="catalog-eyebrow">Design System</p><h1>Inputs</h1><p>Shared single-line and multiline text controls.</p></header><div class="catalog-example-list"><section class="catalog-example"><div class="catalog-example-copy"><h2>Text input</h2><p>Single-line input for names, titles, and short labels.</p></div><div class="catalog-example-preview"><input id="name" type="text" name="name" value="Projet Atlas" placeholder="Nom du projet" class="ui-input"></div><pre class="catalog-example-snippet"><code>@ui.Input(ui.InputProps{
|
||||
<main class="catalog-page"><nav class="catalog-nav" aria-label="Catalog navigation"><a href="./index.html" class="catalog-home-link">Catalog</a><div class="catalog-nav-links"><a href="./tokens.html" class="catalog-nav-link">Tokens</a><a href="./buttons.html" class="catalog-nav-link">Buttons</a><a href="./badges.html" class="catalog-nav-link">Badges</a><a href="./icon-buttons.html" class="catalog-nav-link">Icon Buttons</a><a href="./inputs.html" class="catalog-nav-link is-active">Inputs</a><a href="./form-fields.html" class="catalog-nav-link">Form Fields</a><a href="./modals.html" class="catalog-nav-link">Modals</a><a href="./spacing.html" class="catalog-nav-link">Spacing</a><a href="./tables.html" class="catalog-nav-link">Tables</a><a href="./empty-states.html" class="catalog-nav-link">Empty States</a><a href="./cards.html" class="catalog-nav-link">Cards</a></div></nav><header class="catalog-page-header"><p class="catalog-eyebrow">Design System</p><h1>Inputs</h1><p>Shared single-line and multiline text controls.</p></header><div class="catalog-example-list"><section class="catalog-example"><div class="catalog-example-copy"><h2>Text input</h2><p>Single-line input for names, titles, and short labels.</p></div><div class="catalog-example-preview"><input id="name" type="text" name="name" value="Projet Atlas" placeholder="Nom du projet" class="ui-input"></div><pre class="catalog-example-snippet"><code>@ui.Input(ui.InputProps{
|
||||
Name: "name",
|
||||
Value: "Projet Atlas",
|
||||
Placeholder: "Nom du projet",
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<link rel="stylesheet" href="../../go-backend/static/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<main class="catalog-page"><nav class="catalog-nav" aria-label="Catalog navigation"><a href="./index.html" class="catalog-home-link">Catalog</a><div class="catalog-nav-links"><a href="./tokens.html" class="catalog-nav-link">Tokens</a><a href="./buttons.html" class="catalog-nav-link">Buttons</a><a href="./badges.html" class="catalog-nav-link">Badges</a><a href="./icon-buttons.html" class="catalog-nav-link">Icon Buttons</a><a href="./inputs.html" class="catalog-nav-link">Inputs</a><a href="./form-fields.html" class="catalog-nav-link">Form Fields</a><a href="./modals.html" class="catalog-nav-link is-active">Modals</a><a href="./tables.html" class="catalog-nav-link">Tables</a><a href="./empty-states.html" class="catalog-nav-link">Empty States</a><a href="./cards.html" class="catalog-nav-link">Cards</a></div></nav><header class="catalog-page-header"><p class="catalog-eyebrow">Design System</p><h1>Modals</h1><p>Shared modal shell for focused create, edit, and confirm flows.</p></header><div class="catalog-example-list"><section class="catalog-example"><div class="catalog-example-copy"><h2>Create modal</h2><p>Shared modal shell with a form body and action footer.</p></div><div class="catalog-example-preview"><div class="ui-modal-backdrop"><div class="ui-modal-panel"><div class="ui-modal-header"><h2>Créer un projet</h2></div><div class="ui-modal-body"><div class="ui-form-field"><label for="modal-name" class="ui-form-label">Nom du projet</label> <input id="modal-name" type="text" name="name" value="" placeholder="Nom du projet" class="ui-input"></div></div><div class="ui-modal-actions"><button type="button" class="ui-button ui-button-solid ui-button-neutral ui-button-md">Annuler</button><button type="submit" class="ui-button ui-button-solid ui-button-default ui-button-md">Créer le projet</button></div></div></div></div><pre class="catalog-example-snippet"><code>@ui.Modal(ui.ModalProps{
|
||||
<main class="catalog-page"><nav class="catalog-nav" aria-label="Catalog navigation"><a href="./index.html" class="catalog-home-link">Catalog</a><div class="catalog-nav-links"><a href="./tokens.html" class="catalog-nav-link">Tokens</a><a href="./buttons.html" class="catalog-nav-link">Buttons</a><a href="./badges.html" class="catalog-nav-link">Badges</a><a href="./icon-buttons.html" class="catalog-nav-link">Icon Buttons</a><a href="./inputs.html" class="catalog-nav-link">Inputs</a><a href="./form-fields.html" class="catalog-nav-link">Form Fields</a><a href="./modals.html" class="catalog-nav-link is-active">Modals</a><a href="./spacing.html" class="catalog-nav-link">Spacing</a><a href="./tables.html" class="catalog-nav-link">Tables</a><a href="./empty-states.html" class="catalog-nav-link">Empty States</a><a href="./cards.html" class="catalog-nav-link">Cards</a></div></nav><header class="catalog-page-header"><p class="catalog-eyebrow">Design System</p><h1>Modals</h1><p>Shared modal shell for focused create, edit, and confirm flows.</p></header><div class="catalog-example-list"><section class="catalog-example"><div class="catalog-example-copy"><h2>Create modal</h2><p>Shared modal shell with a form body and action footer.</p></div><div class="catalog-example-preview"><div class="ui-modal-backdrop"><div class="ui-modal-panel"><div class="ui-modal-header"><h2>Créer un projet</h2></div><div class="ui-modal-body"><div class="ui-form-field"><label for="modal-name" class="ui-form-label">Nom du projet</label> <input id="modal-name" type="text" name="name" value="" placeholder="Nom du projet" class="ui-input"></div></div><div class="ui-modal-actions"><button type="button" class="ui-button ui-button-solid ui-button-neutral ui-button-md">Annuler</button><button type="submit" class="ui-button ui-button-solid ui-button-default ui-button-md">Créer le projet</button></div></div></div></div><pre class="catalog-example-snippet"><code>@ui.Modal(ui.ModalProps{
|
||||
Title: "Créer un projet",
|
||||
Body: ui.FormField(...),
|
||||
Actions: ui.Button(...),
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<link rel="stylesheet" href="../../go-backend/static/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<main class="catalog-page"><nav class="catalog-nav" aria-label="Catalog navigation"><a href="./index.html" class="catalog-home-link">Catalog</a><div class="catalog-nav-links"><a href="./tokens.html" class="catalog-nav-link">Tokens</a><a href="./buttons.html" class="catalog-nav-link">Buttons</a><a href="./badges.html" class="catalog-nav-link">Badges</a><a href="./icon-buttons.html" class="catalog-nav-link">Icon Buttons</a><a href="./inputs.html" class="catalog-nav-link">Inputs</a><a href="./form-fields.html" class="catalog-nav-link">Form Fields</a><a href="./modals.html" class="catalog-nav-link">Modals</a><a href="./tables.html" class="catalog-nav-link is-active">Tables</a><a href="./empty-states.html" class="catalog-nav-link">Empty States</a><a href="./cards.html" class="catalog-nav-link">Cards</a></div></nav><header class="catalog-page-header"><p class="catalog-eyebrow">Design System</p><h1>Tables</h1><p>Shared table shell for server-rendered list views.</p></header><div class="catalog-example-list"><section class="catalog-example"><div class="catalog-example-copy"><h2>List shell</h2><p>Shared wrapper for server-rendered resource tables.</p></div><div class="catalog-example-preview"><div class="ui-table-shell"><table class="ui-table"><thead><tr><th class="px-6 py-3 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">Projet</th><th class="px-6 py-3 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">Statut</th></tr></thead> <tbody><tr><td class="px-6 py-4">Table View</td><td class="px-6 py-4">En cours</td></tr></tbody></table></div></div><pre class="catalog-example-snippet"><code>@ui.Table(ui.TableProps{
|
||||
<main class="catalog-page"><nav class="catalog-nav" aria-label="Catalog navigation"><a href="./index.html" class="catalog-home-link">Catalog</a><div class="catalog-nav-links"><a href="./tokens.html" class="catalog-nav-link">Tokens</a><a href="./buttons.html" class="catalog-nav-link">Buttons</a><a href="./badges.html" class="catalog-nav-link">Badges</a><a href="./icon-buttons.html" class="catalog-nav-link">Icon Buttons</a><a href="./inputs.html" class="catalog-nav-link">Inputs</a><a href="./form-fields.html" class="catalog-nav-link">Form Fields</a><a href="./modals.html" class="catalog-nav-link">Modals</a><a href="./spacing.html" class="catalog-nav-link">Spacing</a><a href="./tables.html" class="catalog-nav-link is-active">Tables</a><a href="./empty-states.html" class="catalog-nav-link">Empty States</a><a href="./cards.html" class="catalog-nav-link">Cards</a></div></nav><header class="catalog-page-header"><p class="catalog-eyebrow">Design System</p><h1>Tables</h1><p>Shared table shell for server-rendered list views.</p></header><div class="catalog-example-list"><section class="catalog-example"><div class="catalog-example-copy"><h2>List shell</h2><p>Shared wrapper for server-rendered resource tables.</p></div><div class="catalog-example-preview"><div class="ui-table-shell"><table class="ui-table"><thead><tr><th class="px-6 py-3 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">Projet</th><th class="px-6 py-3 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">Statut</th></tr></thead> <tbody><tr><td class="px-6 py-4">Table View</td><td class="px-6 py-4">En cours</td></tr></tbody></table></div></div><pre class="catalog-example-snippet"><code>@ui.Table(ui.TableProps{
|
||||
Head: TabloListHead(),
|
||||
Body: TabloListBody(tablos),
|
||||
})</code></pre></section></div></main>
|
||||
|
|
|
|||
|
|
@ -8,6 +8,6 @@
|
|||
<link rel="stylesheet" href="../../go-backend/static/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<main class="catalog-page"><nav class="catalog-nav" aria-label="Catalog navigation"><a href="./index.html" class="catalog-home-link">Catalog</a><div class="catalog-nav-links"><a href="./tokens.html" class="catalog-nav-link is-active">Tokens</a><a href="./buttons.html" class="catalog-nav-link">Buttons</a><a href="./badges.html" class="catalog-nav-link">Badges</a><a href="./icon-buttons.html" class="catalog-nav-link">Icon Buttons</a><a href="./inputs.html" class="catalog-nav-link">Inputs</a><a href="./form-fields.html" class="catalog-nav-link">Form Fields</a><a href="./modals.html" class="catalog-nav-link">Modals</a><a href="./tables.html" class="catalog-nav-link">Tables</a><a href="./empty-states.html" class="catalog-nav-link">Empty States</a><a href="./cards.html" class="catalog-nav-link">Cards</a></div></nav><header class="catalog-page-header"><p class="catalog-eyebrow">Design System</p><h1>Tokens</h1><p>Semantic colors and status roles used by the Go design system.</p></header><div class="catalog-example-list"><section class="catalog-example"><div class="catalog-example-copy"><h2>Status tones</h2><p>Shared semantic badges for info, warning, success, and danger states.</p></div><div class="catalog-example-preview"><div class="catalog-inline"><span class="ui-badge ui-badge-info">À faire</span></div><div class="catalog-inline"><span class="ui-badge ui-badge-warning">En cours</span></div><div class="catalog-inline"><span class="ui-badge ui-badge-success">Terminé</span></div><div class="catalog-inline"><span class="ui-badge ui-badge-danger">Erreur</span></div></div><pre class="catalog-example-snippet"><code>@ui.Badge(ui.BadgeProps{Label: "En cours", Variant: ui.BadgeVariantWarning})</code></pre></section></div></main>
|
||||
<main class="catalog-page"><nav class="catalog-nav" aria-label="Catalog navigation"><a href="./index.html" class="catalog-home-link">Catalog</a><div class="catalog-nav-links"><a href="./tokens.html" class="catalog-nav-link is-active">Tokens</a><a href="./buttons.html" class="catalog-nav-link">Buttons</a><a href="./badges.html" class="catalog-nav-link">Badges</a><a href="./icon-buttons.html" class="catalog-nav-link">Icon Buttons</a><a href="./inputs.html" class="catalog-nav-link">Inputs</a><a href="./form-fields.html" class="catalog-nav-link">Form Fields</a><a href="./modals.html" class="catalog-nav-link">Modals</a><a href="./spacing.html" class="catalog-nav-link">Spacing</a><a href="./tables.html" class="catalog-nav-link">Tables</a><a href="./empty-states.html" class="catalog-nav-link">Empty States</a><a href="./cards.html" class="catalog-nav-link">Cards</a></div></nav><header class="catalog-page-header"><p class="catalog-eyebrow">Design System</p><h1>Tokens</h1><p>Semantic colors and status roles used by the Go design system.</p></header><div class="catalog-example-list"><section class="catalog-example"><div class="catalog-example-copy"><h2>Status tones</h2><p>Shared semantic badges for info, warning, success, and danger states.</p></div><div class="catalog-example-preview"><div class="catalog-inline"><span class="ui-badge ui-badge-info">À faire</span></div><div class="catalog-inline"><span class="ui-badge ui-badge-warning">En cours</span></div><div class="catalog-inline"><span class="ui-badge ui-badge-success">Terminé</span></div><div class="catalog-inline"><span class="ui-badge ui-badge-danger">Erreur</span></div></div><pre class="catalog-example-snippet"><code>@ui.Badge(ui.BadgeProps{Label: "En cours", Variant: ui.BadgeVariantWarning})</code></pre></section></div></main>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ This intentionally does not start the broader `status` deprecation effort. `stat
|
|||
- Task-derived status inference
|
||||
- Reworking the current search or filter model
|
||||
- Introducing custom JavaScript beyond the existing HTMX-driven pattern
|
||||
- Adding color pickers, preset palettes, or browser-native advanced color UI
|
||||
- Building a `/tablos/:id` detail edit page
|
||||
|
||||
**User Experience**
|
||||
|
|
@ -46,9 +45,10 @@ On the Go backend `Mes Projets` page:
|
|||
- the modal lets the user update:
|
||||
- `name`
|
||||
- `color`
|
||||
- the edit modal includes a color picker control bound to the same `color` value
|
||||
- `status` is not editable in the create or edit modal for this slice
|
||||
|
||||
The color is entered as a text value using a strict full hex format such as `#3B82F6`.
|
||||
The stored color value remains a strict full hex string such as `#3B82F6`.
|
||||
|
||||
**Data Model**
|
||||
|
||||
|
|
@ -152,6 +152,8 @@ Modal behavior:
|
|||
|
||||
- create modal collects `name` and `color`
|
||||
- edit modal collects `name` and `color`
|
||||
- edit modal includes a native color picker control, prefilled from the current tablo color
|
||||
- the picker and color text input stay in sync so submissions always send one canonical `#RRGGBB` value
|
||||
- both modals render inline validation errors
|
||||
- cancel closes the modal and preserves current page state
|
||||
|
||||
|
|
@ -224,6 +226,13 @@ A pragmatic shape is:
|
|||
|
||||
The exact struct layout can be chosen during implementation, but it should support both modal variants without duplicating page-state plumbing.
|
||||
|
||||
For the edit modal specifically, the view model should provide the current validated hex color so both:
|
||||
|
||||
- the text input
|
||||
- the native color picker
|
||||
|
||||
can render from the same source of truth.
|
||||
|
||||
**Error Handling**
|
||||
|
||||
Create or update validation failure:
|
||||
|
|
@ -254,10 +263,11 @@ Repository coverage:
|
|||
Handler coverage:
|
||||
|
||||
- `GET /tablos` create modal includes `color` field
|
||||
- `GET /tablos/{id}/edit` renders prefilled `name` and `color`
|
||||
- `GET /tablos/{id}/edit` renders prefilled `name`, `color`, and color picker value
|
||||
- `POST /tablos` rejects missing or invalid `color`
|
||||
- `POST /tablos/{id}` rejects missing or invalid `color`
|
||||
- `POST /tablos/{id}` updates visible name and color in returned HTML
|
||||
- edit modal keeps color picker and submitted hex value aligned
|
||||
- grid card markup contains edit action before delete
|
||||
- list row markup contains edit action before delete
|
||||
|
||||
|
|
@ -266,6 +276,7 @@ HTML assertions should verify:
|
|||
- the edit trigger exists with the expected icon/button semantics
|
||||
- the edit trigger appears before delete in the rendered action area
|
||||
- the modal contains both `Nom du projet` and `Couleur`
|
||||
- the edit modal contains a color picker control in addition to the hex color input
|
||||
- invalid hex values return a `422` response with inline feedback mentioning `#RRGGBB`
|
||||
|
||||
**Implementation Notes**
|
||||
|
|
@ -289,6 +300,7 @@ The feature is complete when:
|
|||
- clicking edit opens a modal for the selected tablo
|
||||
- the modal allows changing the tablo `name`
|
||||
- the modal allows changing the tablo `color`
|
||||
- the edit modal includes a color picker for choosing the tablo color
|
||||
- create also accepts `color`
|
||||
- `color` only accepts full 6-digit hex values like `#3B82F6`
|
||||
- successful edits update the rendered project card or list row
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ func TestGenerateSiteWritesExpectedPages(t *testing.T) {
|
|||
"inputs.html",
|
||||
"form-fields.html",
|
||||
"modals.html",
|
||||
"spacing.html",
|
||||
"tables.html",
|
||||
"empty-states.html",
|
||||
"cards.html",
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ INSERT INTO public.tablos (
|
|||
id,
|
||||
owner_id,
|
||||
name,
|
||||
color,
|
||||
status,
|
||||
created_at,
|
||||
updated_at
|
||||
|
|
@ -68,13 +69,14 @@ INSERT INTO public.tablos (
|
|||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5,
|
||||
now(),
|
||||
now()
|
||||
)
|
||||
RETURNING id, owner_id, name, status, created_at, updated_at, deleted_at;
|
||||
RETURNING id, owner_id, name, color, status, created_at, updated_at, deleted_at;
|
||||
|
||||
-- name: ListTablos :many
|
||||
SELECT id, owner_id, name, status, created_at, updated_at, deleted_at
|
||||
SELECT id, owner_id, name, color, status, created_at, updated_at, deleted_at
|
||||
FROM public.tablos
|
||||
WHERE owner_id = sqlc.arg(owner_id)
|
||||
AND deleted_at IS NULL
|
||||
|
|
@ -86,6 +88,15 @@ WHERE owner_id = sqlc.arg(owner_id)
|
|||
)
|
||||
ORDER BY created_at DESC;
|
||||
|
||||
-- name: UpdateTablo :execrows
|
||||
UPDATE public.tablos
|
||||
SET name = $3,
|
||||
color = $4,
|
||||
updated_at = now()
|
||||
WHERE id = $1
|
||||
AND owner_id = $2
|
||||
AND deleted_at IS NULL;
|
||||
|
||||
-- name: SoftDeleteTablo :execrows
|
||||
UPDATE public.tablos
|
||||
SET deleted_at = now(), updated_at = now()
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ type PostgresAuthRepository struct {
|
|||
queries *sqlcdb.Queries
|
||||
}
|
||||
|
||||
const defaultTabloColor = "#3B82F6"
|
||||
|
||||
func NewPostgresAuthRepository(ctx context.Context, databaseURL string) (*PostgresAuthRepository, error) {
|
||||
if databaseURL == "" {
|
||||
return nil, errors.New("DATABASE_URL is required")
|
||||
|
|
@ -149,6 +151,7 @@ func (r *PostgresAuthRepository) CreateTablo(ctx context.Context, input tablomod
|
|||
ID: uuid.New(),
|
||||
OwnerID: input.OwnerID,
|
||||
Name: strings.TrimSpace(input.Name),
|
||||
Color: storedTabloColor(input.Color),
|
||||
Status: string(input.Status),
|
||||
})
|
||||
if err != nil {
|
||||
|
|
@ -177,6 +180,22 @@ func (r *PostgresAuthRepository) ListTablos(ctx context.Context, input tablomode
|
|||
return tablos, nil
|
||||
}
|
||||
|
||||
func (r *PostgresAuthRepository) UpdateTablo(ctx context.Context, input tablomodel.UpdateInput) error {
|
||||
rows, err := r.queries.UpdateTablo(ctx, sqlcdb.UpdateTabloParams{
|
||||
ID: input.ID,
|
||||
OwnerID: input.OwnerID,
|
||||
Name: strings.TrimSpace(input.Name),
|
||||
Color: storedTabloColor(input.Color),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rows == 0 {
|
||||
return tablomodel.ErrNotFound
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *PostgresAuthRepository) SoftDeleteTablo(ctx context.Context, tabloID uuid.UUID, ownerID uuid.UUID) error {
|
||||
rows, err := r.queries.SoftDeleteTablo(ctx, sqlcdb.SoftDeleteTabloParams{
|
||||
ID: tabloID,
|
||||
|
|
@ -202,6 +221,14 @@ func nullableText(value string) pgtype.Text {
|
|||
return pgtype.Text{String: value, Valid: true}
|
||||
}
|
||||
|
||||
func storedTabloColor(value string) string {
|
||||
trimmed := strings.TrimSpace(value)
|
||||
if trimmed == "" {
|
||||
return defaultTabloColor
|
||||
}
|
||||
return trimmed
|
||||
}
|
||||
|
||||
func nullableStatus(value *tablomodel.Status) pgtype.Text {
|
||||
if value == nil {
|
||||
return pgtype.Text{}
|
||||
|
|
@ -214,6 +241,7 @@ func mapTabloRecord(row sqlcdb.Tablo) tablomodel.Record {
|
|||
ID: row.ID,
|
||||
OwnerID: row.OwnerID,
|
||||
Name: row.Name,
|
||||
Color: storedTabloColor(row.Color),
|
||||
Status: tablomodel.Status(row.Status),
|
||||
CreatedAt: row.CreatedAt.Time,
|
||||
UpdatedAt: row.UpdatedAt.Time,
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ CREATE TABLE IF NOT EXISTS public.tablos (
|
|||
id uuid PRIMARY KEY,
|
||||
owner_id uuid NOT NULL REFERENCES public.users(id) ON DELETE CASCADE,
|
||||
name text NOT NULL,
|
||||
color text NOT NULL,
|
||||
status text NOT NULL,
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
updated_at timestamptz NOT NULL DEFAULT now(),
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ type Tablo struct {
|
|||
ID uuid.UUID `db:"id"`
|
||||
OwnerID uuid.UUID `db:"owner_id"`
|
||||
Name string `db:"name"`
|
||||
Color string `db:"color"`
|
||||
Status string `db:"status"`
|
||||
CreatedAt pgtype.Timestamptz `db:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `db:"updated_at"`
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ type Querier interface {
|
|||
GetSessionByToken(ctx context.Context, sessionToken string) (AuthSession, error)
|
||||
ListTablos(ctx context.Context, arg ListTablosParams) ([]Tablo, error)
|
||||
SoftDeleteTablo(ctx context.Context, arg SoftDeleteTabloParams) (int64, error)
|
||||
UpdateTablo(ctx context.Context, arg UpdateTabloParams) (int64, error)
|
||||
}
|
||||
|
||||
var _ Querier = (*Queries)(nil)
|
||||
|
|
|
|||
|
|
@ -90,6 +90,7 @@ INSERT INTO public.tablos (
|
|||
id,
|
||||
owner_id,
|
||||
name,
|
||||
color,
|
||||
status,
|
||||
created_at,
|
||||
updated_at
|
||||
|
|
@ -98,16 +99,18 @@ INSERT INTO public.tablos (
|
|||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5,
|
||||
now(),
|
||||
now()
|
||||
)
|
||||
RETURNING id, owner_id, name, status, created_at, updated_at, deleted_at
|
||||
RETURNING id, owner_id, name, color, status, created_at, updated_at, deleted_at
|
||||
`
|
||||
|
||||
type CreateTabloParams struct {
|
||||
ID uuid.UUID `db:"id"`
|
||||
OwnerID uuid.UUID `db:"owner_id"`
|
||||
Name string `db:"name"`
|
||||
Color string `db:"color"`
|
||||
Status string `db:"status"`
|
||||
}
|
||||
|
||||
|
|
@ -116,6 +119,7 @@ func (q *Queries) CreateTablo(ctx context.Context, arg CreateTabloParams) (Tablo
|
|||
arg.ID,
|
||||
arg.OwnerID,
|
||||
arg.Name,
|
||||
arg.Color,
|
||||
arg.Status,
|
||||
)
|
||||
var i Tablo
|
||||
|
|
@ -123,6 +127,7 @@ func (q *Queries) CreateTablo(ctx context.Context, arg CreateTabloParams) (Tablo
|
|||
&i.ID,
|
||||
&i.OwnerID,
|
||||
&i.Name,
|
||||
&i.Color,
|
||||
&i.Status,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
|
|
@ -214,7 +219,7 @@ func (q *Queries) GetSessionByToken(ctx context.Context, sessionToken string) (A
|
|||
}
|
||||
|
||||
const listTablos = `-- name: ListTablos :many
|
||||
SELECT id, owner_id, name, status, created_at, updated_at, deleted_at
|
||||
SELECT id, owner_id, name, color, status, created_at, updated_at, deleted_at
|
||||
FROM public.tablos
|
||||
WHERE owner_id = $1
|
||||
AND deleted_at IS NULL
|
||||
|
|
@ -246,6 +251,7 @@ func (q *Queries) ListTablos(ctx context.Context, arg ListTablosParams) ([]Tablo
|
|||
&i.ID,
|
||||
&i.OwnerID,
|
||||
&i.Name,
|
||||
&i.Color,
|
||||
&i.Status,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
|
|
@ -281,3 +287,33 @@ func (q *Queries) SoftDeleteTablo(ctx context.Context, arg SoftDeleteTabloParams
|
|||
}
|
||||
return result.RowsAffected(), nil
|
||||
}
|
||||
|
||||
const updateTablo = `-- name: UpdateTablo :execrows
|
||||
UPDATE public.tablos
|
||||
SET name = $3,
|
||||
color = $4,
|
||||
updated_at = now()
|
||||
WHERE id = $1
|
||||
AND owner_id = $2
|
||||
AND deleted_at IS NULL
|
||||
`
|
||||
|
||||
type UpdateTabloParams struct {
|
||||
ID uuid.UUID `db:"id"`
|
||||
OwnerID uuid.UUID `db:"owner_id"`
|
||||
Name string `db:"name"`
|
||||
Color string `db:"color"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateTablo(ctx context.Context, arg UpdateTabloParams) (int64, error) {
|
||||
result, err := q.db.Exec(ctx, updateTablo,
|
||||
arg.ID,
|
||||
arg.OwnerID,
|
||||
arg.Name,
|
||||
arg.Color,
|
||||
)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return result.RowsAffected(), nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ type Record struct {
|
|||
ID uuid.UUID
|
||||
OwnerID uuid.UUID
|
||||
Name string
|
||||
Color string
|
||||
Status Status
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
|
|
@ -30,9 +31,17 @@ type Record struct {
|
|||
type CreateInput struct {
|
||||
OwnerID uuid.UUID
|
||||
Name string
|
||||
Color string
|
||||
Status Status
|
||||
}
|
||||
|
||||
type UpdateInput struct {
|
||||
ID uuid.UUID
|
||||
OwnerID uuid.UUID
|
||||
Name string
|
||||
Color string
|
||||
}
|
||||
|
||||
type ListInput struct {
|
||||
OwnerID uuid.UUID
|
||||
Query string
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ type AuthRepository interface {
|
|||
GetSessionByToken(ctx context.Context, token string) (Session, error)
|
||||
DeleteSessionByToken(ctx context.Context, token string) error
|
||||
CreateTablo(ctx context.Context, input CreateTabloInput) (TabloRecord, error)
|
||||
UpdateTablo(ctx context.Context, input UpdateTabloInput) error
|
||||
ListTablos(ctx context.Context, input ListTablosInput) ([]TabloRecord, error)
|
||||
SoftDeleteTablo(ctx context.Context, tabloID uuid.UUID, ownerID uuid.UUID) error
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
|
@ -16,6 +17,11 @@ import (
|
|||
|
||||
var ErrTabloNotFound = tablomodel.ErrNotFound
|
||||
|
||||
var tabloColorPattern = regexp.MustCompile(`^#[0-9A-Fa-f]{6}$`)
|
||||
|
||||
const defaultTabloColor = "#3B82F6"
|
||||
const tabloColorValidationMessage = "La couleur du projet doit être un code hexadécimal au format #RRGGBB"
|
||||
|
||||
type TabloStatus = tablomodel.Status
|
||||
|
||||
const (
|
||||
|
|
@ -26,13 +32,15 @@ const (
|
|||
|
||||
type TabloRecord = tablomodel.Record
|
||||
type CreateTabloInput = tablomodel.CreateInput
|
||||
type UpdateTabloInput = tablomodel.UpdateInput
|
||||
type ListTablosInput = tablomodel.ListInput
|
||||
|
||||
type TablosPageState struct {
|
||||
View string
|
||||
Query string
|
||||
Status string
|
||||
ModalOpen bool
|
||||
View string
|
||||
Query string
|
||||
Status string
|
||||
ModalKind string
|
||||
EditingTabloID string
|
||||
}
|
||||
|
||||
func normalizeTabloQuery(query string) string {
|
||||
|
|
@ -58,7 +66,7 @@ func parseTablosPageState(values interface {
|
|||
View: view,
|
||||
Query: strings.TrimSpace(values.Get("q")),
|
||||
Status: status,
|
||||
ModalOpen: strings.TrimSpace(values.Get("modal")) == "create",
|
||||
ModalKind: normalizedModalKind(strings.TrimSpace(values.Get("modal"))),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -78,6 +86,30 @@ func (s TablosPageState) statusFilter() *TabloStatus {
|
|||
}
|
||||
}
|
||||
|
||||
func normalizedModalKind(kind string) string {
|
||||
switch kind {
|
||||
case "create", "edit":
|
||||
return kind
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func normalizeTabloColor(raw string) (string, bool) {
|
||||
color := strings.TrimSpace(raw)
|
||||
if !tabloColorPattern.MatchString(color) {
|
||||
return "", false
|
||||
}
|
||||
return strings.ToUpper(color), true
|
||||
}
|
||||
|
||||
func storedTabloColor(raw string) string {
|
||||
if color, ok := normalizeTabloColor(raw); ok {
|
||||
return color
|
||||
}
|
||||
return defaultTabloColor
|
||||
}
|
||||
|
||||
func (h *AuthHandler) PostTablos() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
user, ok := h.authenticatedUser(r.Context(), r)
|
||||
|
|
@ -92,35 +124,153 @@ func (h *AuthHandler) PostTablos() http.HandlerFunc {
|
|||
}
|
||||
|
||||
state := parseTablosPageState(r.Form)
|
||||
state.ModalOpen = true
|
||||
state.ModalKind = "create"
|
||||
|
||||
name := strings.TrimSpace(r.FormValue("name"))
|
||||
if name == "" {
|
||||
renderTablosResponse(w, r, "/tablos", tablosPageViewModel(user, state, nil, name, "Le nom du projet est requis"), http.StatusUnprocessableEntity)
|
||||
tablos, err := listTablosForState(r.Context(), h.repo, user.ID, state)
|
||||
if err != nil {
|
||||
http.Error(w, "failed to list tablos", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
renderTablosResponse(w, r, "/tablos", tablosPageViewModel(user, state, tablos, name, r.FormValue("color"), "Le nom du projet est requis"), http.StatusUnprocessableEntity)
|
||||
return
|
||||
}
|
||||
|
||||
color, ok := normalizeTabloColor(r.FormValue("color"))
|
||||
if !ok {
|
||||
tablos, err := listTablosForState(r.Context(), h.repo, user.ID, state)
|
||||
if err != nil {
|
||||
http.Error(w, "failed to list tablos", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
renderTablosResponse(w, r, "/tablos", tablosPageViewModel(user, state, tablos, name, r.FormValue("color"), tabloColorValidationMessage), http.StatusUnprocessableEntity)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := h.repo.CreateTablo(r.Context(), CreateTabloInput{
|
||||
OwnerID: user.ID,
|
||||
Name: name,
|
||||
Color: color,
|
||||
Status: TabloStatusTodo,
|
||||
}); err != nil {
|
||||
http.Error(w, "failed to create tablo", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
state.ModalOpen = false
|
||||
tablos, err := h.repo.ListTablos(r.Context(), ListTablosInput{
|
||||
OwnerID: user.ID,
|
||||
Query: state.Query,
|
||||
Status: state.statusFilter(),
|
||||
})
|
||||
state.ModalKind = ""
|
||||
tablos, err := listTablosForState(r.Context(), h.repo, user.ID, state)
|
||||
if err != nil {
|
||||
http.Error(w, "failed to list tablos", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
renderTablosResponse(w, r, "/tablos", tablosPageViewModel(user, state, tablos, "", ""), http.StatusOK)
|
||||
renderTablosResponse(w, r, "/tablos", tablosPageViewModel(user, state, tablos, "", "", ""), http.StatusOK)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *AuthHandler) GetEditTabloModal() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
user, ok := h.authenticatedUser(r.Context(), r)
|
||||
if !ok {
|
||||
http.Redirect(w, r, "/login", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
|
||||
tabloID, err := uuid.Parse(r.PathValue("tabloID"))
|
||||
if err != nil {
|
||||
http.Error(w, "invalid tablo id", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
state := parseTablosPageState(r.URL.Query())
|
||||
state.ModalKind = "edit"
|
||||
state.EditingTabloID = tabloID.String()
|
||||
|
||||
tablos, err := listTablosForState(r.Context(), h.repo, user.ID, state)
|
||||
if err != nil {
|
||||
http.Error(w, "failed to list tablos", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
tablo, ok := findTabloByID(tablos, tabloID)
|
||||
if !ok {
|
||||
http.Error(w, "tablo not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
renderTablosResponse(w, r, "/tablos", tablosPageViewModel(user, state, tablos, tablo.Name, tablo.Color, ""), http.StatusOK)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *AuthHandler) PostTabloUpdate() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
user, ok := h.authenticatedUser(r.Context(), r)
|
||||
if !ok {
|
||||
http.Redirect(w, r, "/login", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
|
||||
tabloID, err := uuid.Parse(r.PathValue("tabloID"))
|
||||
if err != nil {
|
||||
http.Error(w, "invalid tablo id", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if err := r.ParseForm(); err != nil {
|
||||
http.Error(w, "invalid form payload", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
state := parseTablosPageState(r.Form)
|
||||
state.ModalKind = "edit"
|
||||
state.EditingTabloID = tabloID.String()
|
||||
|
||||
name := strings.TrimSpace(r.FormValue("name"))
|
||||
if name == "" {
|
||||
tablos, err := listTablosForState(r.Context(), h.repo, user.ID, state)
|
||||
if err != nil {
|
||||
http.Error(w, "failed to list tablos", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
renderTablosResponse(w, r, "/tablos", tablosPageViewModel(user, state, tablos, name, r.FormValue("color"), "Le nom du projet est requis"), http.StatusUnprocessableEntity)
|
||||
return
|
||||
}
|
||||
|
||||
color, colorOK := normalizeTabloColor(r.FormValue("color"))
|
||||
if !colorOK {
|
||||
tablos, err := listTablosForState(r.Context(), h.repo, user.ID, state)
|
||||
if err != nil {
|
||||
http.Error(w, "failed to list tablos", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
renderTablosResponse(w, r, "/tablos", tablosPageViewModel(user, state, tablos, name, r.FormValue("color"), tabloColorValidationMessage), http.StatusUnprocessableEntity)
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.repo.UpdateTablo(r.Context(), UpdateTabloInput{
|
||||
ID: tabloID,
|
||||
OwnerID: user.ID,
|
||||
Name: name,
|
||||
Color: color,
|
||||
}); err != nil {
|
||||
if errors.Is(err, ErrTabloNotFound) {
|
||||
http.Error(w, "tablo not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
http.Error(w, "failed to update tablo", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
state.ModalKind = ""
|
||||
state.EditingTabloID = ""
|
||||
tablos, err := listTablosForState(r.Context(), h.repo, user.ID, state)
|
||||
if err != nil {
|
||||
http.Error(w, "failed to list tablos", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
renderTablosResponse(w, r, "/tablos", tablosPageViewModel(user, state, tablos, "", "", ""), http.StatusOK)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -148,17 +298,13 @@ func (h *AuthHandler) DeleteTablo() http.HandlerFunc {
|
|||
}
|
||||
|
||||
state := parseTablosPageState(r.URL.Query())
|
||||
tablos, err := h.repo.ListTablos(r.Context(), ListTablosInput{
|
||||
OwnerID: user.ID,
|
||||
Query: state.Query,
|
||||
Status: state.statusFilter(),
|
||||
})
|
||||
tablos, err := listTablosForState(r.Context(), h.repo, user.ID, state)
|
||||
if err != nil {
|
||||
http.Error(w, "failed to list tablos", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
renderTablosResponse(w, r, "/tablos", tablosPageViewModel(user, state, tablos, "", ""), http.StatusOK)
|
||||
renderTablosResponse(w, r, "/tablos", tablosPageViewModel(user, state, tablos, "", "", ""), http.StatusOK)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -170,32 +316,47 @@ func (h *AuthHandler) renderTablosPage(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
state := parseTablosPageState(r.URL.Query())
|
||||
tablos, err := h.repo.ListTablos(r.Context(), ListTablosInput{
|
||||
OwnerID: user.ID,
|
||||
Query: state.Query,
|
||||
Status: state.statusFilter(),
|
||||
})
|
||||
tablos, err := listTablosForState(r.Context(), h.repo, user.ID, state)
|
||||
if err != nil {
|
||||
http.Error(w, "failed to list tablos", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
renderTablosResponse(w, r, "/tablos", tablosPageViewModel(user, state, tablos, "", ""), http.StatusOK)
|
||||
renderTablosResponse(w, r, "/tablos", tablosPageViewModel(user, state, tablos, "", "", ""), http.StatusOK)
|
||||
}
|
||||
|
||||
func tablosPageViewModel(user PublicUser, state TablosPageState, tablos []TabloRecord, formName string, errorMessage string) views.TablosPageViewModel {
|
||||
func tablosPageViewModel(user PublicUser, state TablosPageState, tablos []TabloRecord, formName string, formColor string, errorMessage string) views.TablosPageViewModel {
|
||||
return views.NewTablosPageViewModel(
|
||||
user.DisplayName,
|
||||
state.View,
|
||||
state.Query,
|
||||
state.Status,
|
||||
state.ModalOpen,
|
||||
state.ModalKind,
|
||||
state.EditingTabloID,
|
||||
formName,
|
||||
formColor,
|
||||
errorMessage,
|
||||
buildTabloCardViews(tablos, state),
|
||||
)
|
||||
}
|
||||
|
||||
func listTablosForState(ctx context.Context, repo AuthRepository, ownerID uuid.UUID, state TablosPageState) ([]TabloRecord, error) {
|
||||
return repo.ListTablos(ctx, ListTablosInput{
|
||||
OwnerID: ownerID,
|
||||
Query: state.Query,
|
||||
Status: state.statusFilter(),
|
||||
})
|
||||
}
|
||||
|
||||
func findTabloByID(tablos []TabloRecord, targetID uuid.UUID) (TabloRecord, bool) {
|
||||
for _, tablo := range tablos {
|
||||
if tablo.ID == targetID {
|
||||
return tablo, true
|
||||
}
|
||||
}
|
||||
return TabloRecord{}, false
|
||||
}
|
||||
|
||||
func renderTablosResponse(w http.ResponseWriter, r *http.Request, activePath string, vm views.TablosPageViewModel, statusCode int) {
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
w.WriteHeader(statusCode)
|
||||
|
|
@ -221,6 +382,7 @@ func (r *InMemoryAuthRepository) CreateTablo(_ context.Context, input CreateTabl
|
|||
ID: uuid.New(),
|
||||
OwnerID: input.OwnerID,
|
||||
Name: strings.TrimSpace(input.Name),
|
||||
Color: storedTabloColor(input.Color),
|
||||
Status: input.Status,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
|
|
@ -258,6 +420,22 @@ func (r *InMemoryAuthRepository) ListTablos(_ context.Context, input ListTablosI
|
|||
return tablos, nil
|
||||
}
|
||||
|
||||
func (r *InMemoryAuthRepository) UpdateTablo(_ context.Context, input UpdateTabloInput) error {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
tablo, ok := r.tablos[input.ID]
|
||||
if !ok || tablo.OwnerID != input.OwnerID || tablo.DeletedAt != nil {
|
||||
return ErrTabloNotFound
|
||||
}
|
||||
|
||||
tablo.Name = strings.TrimSpace(input.Name)
|
||||
tablo.Color = storedTabloColor(input.Color)
|
||||
tablo.UpdatedAt = time.Now().UTC()
|
||||
r.tablos[input.ID] = tablo
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *InMemoryAuthRepository) SoftDeleteTablo(_ context.Context, tabloID uuid.UUID, ownerID uuid.UUID) error {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
|
@ -293,6 +471,7 @@ func buildTabloCardViews(tablos []TabloRecord, state TablosPageState) []views.Ta
|
|||
items = append(items, views.TabloCardView{
|
||||
ID: tablo.ID.String(),
|
||||
Name: tablo.Name,
|
||||
Color: storedTabloColor(tablo.Color),
|
||||
Status: string(tablo.Status),
|
||||
StatusLabel: statusLabel,
|
||||
StatusClass: statusClass,
|
||||
|
|
@ -303,6 +482,7 @@ func buildTabloCardViews(tablos []TabloRecord, state TablosPageState) []views.Ta
|
|||
ProgressLabel: fmt.Sprintf("%d%%", progress),
|
||||
DeleteURL: "/tablos/" + tablo.ID.String(),
|
||||
DeleteRequestURL: buildDeleteRequestURL("/tablos/"+tablo.ID.String(), state),
|
||||
EditRequestURL: buildEditRequestURL("/tablos/"+tablo.ID.String()+"/edit", state),
|
||||
IconKind: iconKind,
|
||||
IconBgClass: bgClass,
|
||||
IconFgClass: fgClass,
|
||||
|
|
@ -314,6 +494,14 @@ func buildTabloCardViews(tablos []TabloRecord, state TablosPageState) []views.Ta
|
|||
}
|
||||
|
||||
func buildDeleteRequestURL(path string, state TablosPageState) string {
|
||||
return buildStatefulRequestURL(path, state)
|
||||
}
|
||||
|
||||
func buildEditRequestURL(path string, state TablosPageState) string {
|
||||
return buildStatefulRequestURL(path, state)
|
||||
}
|
||||
|
||||
func buildStatefulRequestURL(path string, state TablosPageState) string {
|
||||
values := url.Values{}
|
||||
values.Set("view", state.View)
|
||||
values.Set("status", state.Status)
|
||||
|
|
|
|||
|
|
@ -127,6 +127,28 @@ func TestInMemoryTablosSoftDeleteRejectsDifferentOwner(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestInMemoryTablosCreatePersistsColor(t *testing.T) {
|
||||
repo := NewInMemoryAuthRepository()
|
||||
user, err := repo.GetAuthUserByEmail(context.Background(), "demo@xtablo.com")
|
||||
if err != nil {
|
||||
t.Fatalf("expected demo user, got error %v", err)
|
||||
}
|
||||
|
||||
created, err := repo.CreateTablo(context.Background(), CreateTabloInput{
|
||||
OwnerID: user.ID,
|
||||
Name: "Roadmap",
|
||||
Color: "#3B82F6",
|
||||
Status: TabloStatusTodo,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("create colored tablo: %v", err)
|
||||
}
|
||||
|
||||
if created.Color != "#3B82F6" {
|
||||
t.Fatalf("expected color to persist, got %q", created.Color)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTablosPageDefaultsToGridAndAllStatus(t *testing.T) {
|
||||
handler := newTestAuthHandler(t)
|
||||
sessionCookie := loginTestUser(t, handler, "demo@xtablo.com", "xtablo-demo")
|
||||
|
|
@ -376,7 +398,7 @@ func TestGetTablosPageListViewUsesDirectTableIconMarkup(t *testing.T) {
|
|||
body := rec.Body.String()
|
||||
for _, want := range []string{
|
||||
`class="flex items-center gap-1.5 [&>svg]:w-4 [&>svg]:h-4 [&>svg]:shrink-0"><svg viewBox="0 0 24 24"`,
|
||||
`class="borderless-icon-button"`,
|
||||
`class="borderless-icon-button ui-icon-button-ghost ui-icon-button-danger" aria-label="Supprimer le projet"`,
|
||||
`class="lucide lucide-trash2 w-4 h-4"`,
|
||||
} {
|
||||
if !strings.Contains(body, want) {
|
||||
|
|
@ -454,11 +476,12 @@ func TestGetTablosPageGridUsesProjectCardMarkup(t *testing.T) {
|
|||
|
||||
body := rec.Body.String()
|
||||
for _, want := range []string{
|
||||
`<article class="project-card">`,
|
||||
`<article class="project-card" style="--project-color:`,
|
||||
`class="project-card-top"`,
|
||||
`class="borderless-icon-button"`,
|
||||
`class="borderless-icon-button ui-icon-button-ghost ui-icon-button-neutral" aria-label="Modifier le projet"`,
|
||||
`class="borderless-icon-button ui-icon-button-ghost ui-icon-button-danger" aria-label="Supprimer le projet"`,
|
||||
`class="project-card-title-row"`,
|
||||
`class="project-avatar project-accent-`,
|
||||
`class="project-avatar"`,
|
||||
`class="project-date-row"`,
|
||||
`class="project-progress-track"`,
|
||||
} {
|
||||
|
|
@ -474,6 +497,7 @@ func TestPostTablosCreatesTodoTablo(t *testing.T) {
|
|||
|
||||
form := url.Values{}
|
||||
form.Set("name", "Roadmap")
|
||||
form.Set("color", "#3B82F6")
|
||||
form.Set("view", "grid")
|
||||
form.Set("status", "all")
|
||||
|
||||
|
|
@ -518,6 +542,152 @@ func TestPostTablosWithEmptyNameReturns422(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestPostTablosWithMissingColorReturns422(t *testing.T) {
|
||||
handler := newTestAuthHandler(t)
|
||||
sessionCookie := loginTestUser(t, handler, "demo@xtablo.com", "xtablo-demo")
|
||||
|
||||
form := url.Values{}
|
||||
form.Set("name", "Roadmap")
|
||||
|
||||
req := httptest.NewRequest(http.MethodPost, "/tablos", strings.NewReader(form.Encode()))
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
req.AddCookie(sessionCookie)
|
||||
rec := httptest.NewRecorder()
|
||||
|
||||
handler.PostTablos().ServeHTTP(rec, req)
|
||||
|
||||
if rec.Code != http.StatusUnprocessableEntity {
|
||||
t.Fatalf("expected status 422, got %d", rec.Code)
|
||||
}
|
||||
|
||||
if !strings.Contains(rec.Body.String(), "La couleur du projet doit être un code hexadécimal au format #RRGGBB") {
|
||||
t.Fatalf("expected color validation error, got %q", rec.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostTablosWithInvalidColorReturns422(t *testing.T) {
|
||||
handler := newTestAuthHandler(t)
|
||||
sessionCookie := loginTestUser(t, handler, "demo@xtablo.com", "xtablo-demo")
|
||||
|
||||
form := url.Values{}
|
||||
form.Set("name", "Roadmap")
|
||||
form.Set("color", "#0af")
|
||||
|
||||
req := httptest.NewRequest(http.MethodPost, "/tablos", strings.NewReader(form.Encode()))
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
req.AddCookie(sessionCookie)
|
||||
rec := httptest.NewRecorder()
|
||||
|
||||
handler.PostTablos().ServeHTTP(rec, req)
|
||||
|
||||
if rec.Code != http.StatusUnprocessableEntity {
|
||||
t.Fatalf("expected status 422, got %d", rec.Code)
|
||||
}
|
||||
|
||||
if !strings.Contains(rec.Body.String(), "La couleur du projet doit être un code hexadécimal au format #RRGGBB") {
|
||||
t.Fatalf("expected invalid color validation error, got %q", rec.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTablosEditModalPrefillsNameAndColorPicker(t *testing.T) {
|
||||
repo := NewInMemoryAuthRepository()
|
||||
handler := NewAuthHandler(repo)
|
||||
sessionCookie := loginTestUser(t, handler, "demo@xtablo.com", "xtablo-demo")
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
req.AddCookie(sessionCookie)
|
||||
userID, ok := handler.currentUserID(req.Context(), req)
|
||||
if !ok {
|
||||
t.Fatal("expected user session")
|
||||
}
|
||||
|
||||
tablo, err := repo.CreateTablo(context.Background(), CreateTabloInput{
|
||||
OwnerID: userID,
|
||||
Name: "Palette",
|
||||
Color: "#3B82F6",
|
||||
Status: TabloStatusTodo,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("create tablo: %v", err)
|
||||
}
|
||||
|
||||
editReq := httptest.NewRequest(http.MethodGet, "/tablos/"+tablo.ID.String()+"/edit?view=grid&status=all", nil)
|
||||
editReq.SetPathValue("tabloID", tablo.ID.String())
|
||||
editReq.AddCookie(sessionCookie)
|
||||
rec := httptest.NewRecorder()
|
||||
|
||||
handler.GetEditTabloModal().ServeHTTP(rec, editReq)
|
||||
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("expected status 200, got %d", rec.Code)
|
||||
}
|
||||
|
||||
body := rec.Body.String()
|
||||
for _, want := range []string{
|
||||
"Palette",
|
||||
`value="#3B82F6"`,
|
||||
`type="color"`,
|
||||
} {
|
||||
if !strings.Contains(body, want) {
|
||||
t.Fatalf("expected edit modal to contain %q, got %q", want, body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostTablosUpdateRejectsDifferentOwner(t *testing.T) {
|
||||
repo := NewInMemoryAuthRepository()
|
||||
handler := NewAuthHandler(repo)
|
||||
|
||||
ownerCookie := loginTestUser(t, handler, "demo@xtablo.com", "xtablo-demo")
|
||||
ownerReq := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
ownerReq.AddCookie(ownerCookie)
|
||||
ownerID, ok := handler.currentUserID(ownerReq.Context(), ownerReq)
|
||||
if !ok {
|
||||
t.Fatal("expected owner session")
|
||||
}
|
||||
|
||||
tablo, err := repo.CreateTablo(context.Background(), CreateTabloInput{
|
||||
OwnerID: ownerID,
|
||||
Name: "Owned",
|
||||
Color: "#3B82F6",
|
||||
Status: TabloStatusTodo,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("create tablo: %v", err)
|
||||
}
|
||||
|
||||
passwordHash, err := hashPassword("xtablo-demo")
|
||||
if err != nil {
|
||||
t.Fatalf("hash password: %v", err)
|
||||
}
|
||||
|
||||
_, err = repo.CreateAuthUser(context.Background(), CreateAuthUserInput{
|
||||
Email: "other@xtablo.com",
|
||||
EncryptedPassword: passwordHash,
|
||||
DisplayName: "other",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("create user: %v", err)
|
||||
}
|
||||
|
||||
otherCookie := loginTestUser(t, handler, "other@xtablo.com", "xtablo-demo")
|
||||
form := url.Values{}
|
||||
form.Set("name", "Updated")
|
||||
form.Set("color", "#EF4444")
|
||||
|
||||
updateReq := httptest.NewRequest(http.MethodPost, "/tablos/"+tablo.ID.String(), strings.NewReader(form.Encode()))
|
||||
updateReq.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
updateReq.SetPathValue("tabloID", tablo.ID.String())
|
||||
updateReq.AddCookie(otherCookie)
|
||||
rec := httptest.NewRecorder()
|
||||
|
||||
handler.PostTabloUpdate().ServeHTTP(rec, updateReq)
|
||||
|
||||
if rec.Code != http.StatusNotFound {
|
||||
t.Fatalf("expected status 404, got %d", rec.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteTabloSoftDeletesOwnedRow(t *testing.T) {
|
||||
repo := NewInMemoryAuthRepository()
|
||||
handler := NewAuthHandler(repo)
|
||||
|
|
|
|||
|
|
@ -36,14 +36,13 @@ func TestPagesIncludeTokensAndButtons(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestPagesIncludePrimitiveCatalogCoverage(t *testing.T) {
|
||||
pages := Pages()
|
||||
|
||||
for _, slug := range []string{
|
||||
"badges",
|
||||
"icon-buttons",
|
||||
"inputs",
|
||||
"form-fields",
|
||||
"modals",
|
||||
"spacing",
|
||||
"tables",
|
||||
"empty-states",
|
||||
"cards",
|
||||
|
|
@ -52,10 +51,6 @@ func TestPagesIncludePrimitiveCatalogCoverage(t *testing.T) {
|
|||
t.Fatalf("expected catalog page %q", slug)
|
||||
}
|
||||
}
|
||||
|
||||
if len(pages) < 10 {
|
||||
t.Fatalf("expected expanded primitive catalog, got %d pages", len(pages))
|
||||
}
|
||||
}
|
||||
|
||||
func TestButtonPageExamplesRenderRealPrimitives(t *testing.T) {
|
||||
|
|
@ -164,6 +159,23 @@ func TestPrimitiveExamplesRenderRealMarkup(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestSpacingPageRendersSpacingPrimitives(t *testing.T) {
|
||||
page, ok := FindPage("spacing")
|
||||
if !ok {
|
||||
t.Fatal("expected spacing page")
|
||||
}
|
||||
|
||||
html := renderToString(t, CatalogPage(page))
|
||||
for _, want := range []string{
|
||||
`ui-space-x`,
|
||||
`ui-space-y`,
|
||||
} {
|
||||
if !strings.Contains(html, want) {
|
||||
t.Fatalf("spacing page expected %q in %q", want, html)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func renderToString(t *testing.T, component templ.Component) string {
|
||||
t.Helper()
|
||||
|
||||
|
|
|
|||
|
|
@ -123,13 +123,33 @@ func iconButtonExamples() []Example {
|
|||
Preview: ui.IconButton(ui.IconButtonProps{
|
||||
Label: "Supprimer le projet",
|
||||
Icon: "trash",
|
||||
Variant: ui.IconButtonVariantDangerGhost,
|
||||
Variant: ui.IconButtonVariantDanger,
|
||||
Tone: ui.IconButtonToneGhost,
|
||||
Type: "button",
|
||||
}),
|
||||
Snippet: `@ui.IconButton(ui.IconButtonProps{
|
||||
Label: "Supprimer le projet",
|
||||
Icon: "trash",
|
||||
Variant: ui.IconButtonVariantDangerGhost,
|
||||
Variant: ui.IconButtonVariantDanger,
|
||||
Tone: ui.IconButtonToneGhost,
|
||||
Type: "button",
|
||||
})`,
|
||||
},
|
||||
{
|
||||
Title: "Borderless neutral action",
|
||||
Description: "Used for lightweight edit or details actions inside cards and list rows.",
|
||||
Preview: ui.IconButton(ui.IconButtonProps{
|
||||
Label: "Modifier le projet",
|
||||
Icon: "pencil",
|
||||
Variant: ui.IconButtonVariantNeutral,
|
||||
Tone: ui.IconButtonToneGhost,
|
||||
Type: "button",
|
||||
}),
|
||||
Snippet: `@ui.IconButton(ui.IconButtonProps{
|
||||
Label: "Modifier le projet",
|
||||
Icon: "pencil",
|
||||
Variant: ui.IconButtonVariantNeutral,
|
||||
Tone: ui.IconButtonToneGhost,
|
||||
Type: "button",
|
||||
})`,
|
||||
},
|
||||
|
|
@ -247,6 +267,67 @@ func modalExamples() []Example {
|
|||
}
|
||||
}
|
||||
|
||||
func spacingExamples() []Example {
|
||||
return []Example{
|
||||
{
|
||||
Title: "Horizontal spacing",
|
||||
Description: "Use SpaceX to insert fixed horizontal gaps between inline or row-aligned components.",
|
||||
Preview: componentFunc(func(ctx context.Context, w io.Writer) error {
|
||||
if _, err := io.WriteString(w, `<div class="catalog-spacing-row">`); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, component := range []templ.Component{
|
||||
ui.Button(ui.ButtonProps{
|
||||
Label: "Précédent",
|
||||
Variant: ui.ButtonVariantNeutral,
|
||||
Size: ui.SizeMD,
|
||||
Type: "button",
|
||||
}),
|
||||
ui.SpaceX(ui.SpaceProps{Size: ui.SpacingStepLG}),
|
||||
ui.Button(ui.ButtonProps{
|
||||
Label: "Suivant",
|
||||
Variant: ui.ButtonVariantDefault,
|
||||
Size: ui.SizeMD,
|
||||
Type: "button",
|
||||
}),
|
||||
} {
|
||||
if err := component.Render(ctx, w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_, err := io.WriteString(w, `</div>`)
|
||||
return err
|
||||
}),
|
||||
Snippet: `@ui.SpaceX(ui.SpaceProps{Size: ui.SpacingStepLG})`,
|
||||
},
|
||||
{
|
||||
Title: "Vertical spacing",
|
||||
Description: "Use SpaceY to insert fixed vertical gaps between stacked blocks.",
|
||||
Preview: componentFunc(func(ctx context.Context, w io.Writer) error {
|
||||
if _, err := io.WriteString(w, `<div class="catalog-spacing-column">`); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, component := range []templ.Component{
|
||||
ui.Card(ui.CardProps{
|
||||
Body: textComponent("Bloc 1"),
|
||||
}),
|
||||
ui.SpaceY(ui.SpaceProps{Size: ui.SpacingStepMD}),
|
||||
ui.Card(ui.CardProps{
|
||||
Body: textComponent("Bloc 2"),
|
||||
}),
|
||||
} {
|
||||
if err := component.Render(ctx, w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_, err := io.WriteString(w, `</div>`)
|
||||
return err
|
||||
}),
|
||||
Snippet: `@ui.SpaceY(ui.SpaceProps{Size: ui.SpacingStepMD})`,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func tableExamples() []Example {
|
||||
return []Example{
|
||||
{
|
||||
|
|
|
|||
|
|
@ -60,6 +60,12 @@ func Pages() []Page {
|
|||
Description: "Shared modal shell for focused create, edit, and confirm flows.",
|
||||
Examples: modalExamples(),
|
||||
},
|
||||
{
|
||||
Slug: "spacing",
|
||||
Title: "Spacing",
|
||||
Description: "Fixed horizontal and vertical spacer primitives for composing gaps between UI components.",
|
||||
Examples: spacingExamples(),
|
||||
},
|
||||
{
|
||||
Slug: "tables",
|
||||
Title: "Tables",
|
||||
|
|
|
|||
|
|
@ -4,12 +4,13 @@ type IconButtonProps struct {
|
|||
Label string
|
||||
Icon string
|
||||
Variant IconButtonVariant
|
||||
Tone IconButtonTone
|
||||
Type string
|
||||
Attrs templ.Attributes
|
||||
}
|
||||
|
||||
templ IconButton(props IconButtonProps) {
|
||||
<button type={ buttonType(props.Type) } class={ iconButtonClass(props.Variant) } aria-label={ props.Label } { props.Attrs... }>
|
||||
<button type={ buttonType(props.Type) } class={ iconButtonClass(props.Variant, props.Tone) } aria-label={ props.Label } { props.Attrs... }>
|
||||
@UIIcon(props.Icon)
|
||||
</button>
|
||||
}
|
||||
|
|
@ -54,6 +55,11 @@ templ UIIcon(kind string) {
|
|||
<rect width="18" height="18" x="3" y="4" rx="2"></rect>
|
||||
<path d="M3 10h18"></path>
|
||||
</svg>
|
||||
case "pencil":
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<path d="M12 20h9"></path>
|
||||
<path d="M16.5 3.5a2.12 2.12 0 1 1 3 3L7 19l-4 1 1-4Z"></path>
|
||||
</svg>
|
||||
case "trash":
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-trash2 w-4 h-4" aria-hidden="true">
|
||||
<path d="M3 6h18"></path>
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ type IconButtonProps struct {
|
|||
Label string
|
||||
Icon string
|
||||
Variant IconButtonVariant
|
||||
Tone IconButtonTone
|
||||
Type string
|
||||
Attrs templ.Attributes
|
||||
}
|
||||
|
|
@ -37,7 +38,7 @@ func IconButton(props IconButtonProps) templ.Component {
|
|||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
var templ_7745c5c3_Var2 = []any{iconButtonClass(props.Variant)}
|
||||
var templ_7745c5c3_Var2 = []any{iconButtonClass(props.Variant, props.Tone)}
|
||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var2...)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
|
|
@ -49,7 +50,7 @@ func IconButton(props IconButtonProps) templ.Component {
|
|||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(buttonType(props.Type))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/ui/icon_button.templ`, Line: 12, Col: 38}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/ui/icon_button.templ`, Line: 13, Col: 38}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
|
|
@ -75,7 +76,7 @@ func IconButton(props IconButtonProps) templ.Component {
|
|||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(props.Label)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/ui/icon_button.templ`, Line: 12, Col: 106}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/ui/icon_button.templ`, Line: 13, Col: 118}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
|
|
@ -157,26 +158,31 @@ func UIIcon(kind string) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
case "pencil":
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M12 20h9\"></path> <path d=\"M16.5 3.5a2.12 2.12 0 1 1 3 3L7 19l-4 1 1-4Z\"></path></svg>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
case "trash":
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-trash2 w-4 h-4\" aria-hidden=\"true\"><path d=\"M3 6h18\"></path> <path d=\"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6\"></path> <path d=\"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2\"></path> <line x1=\"10\" x2=\"10\" y1=\"11\" y2=\"17\"></line> <line x1=\"14\" x2=\"14\" y1=\"11\" y2=\"17\"></line></svg>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-trash2 w-4 h-4\" aria-hidden=\"true\"><path d=\"M3 6h18\"></path> <path d=\"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6\"></path> <path d=\"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2\"></path> <line x1=\"10\" x2=\"10\" y1=\"11\" y2=\"17\"></line> <line x1=\"14\" x2=\"14\" y1=\"11\" y2=\"17\"></line></svg>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
default:
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "<span aria-hidden=\"true\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "<span aria-hidden=\"true\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var7 string
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(kind)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/ui/icon_button.templ`, Line: 66, Col: 34}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/ui/icon_button.templ`, Line: 72, Col: 34}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</span>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</span>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,7 +40,8 @@ func TestIconButtonRendersBorderlessDestructiveMarkup(t *testing.T) {
|
|||
component := IconButton(IconButtonProps{
|
||||
Label: "Supprimer le projet",
|
||||
Icon: "trash",
|
||||
Variant: IconButtonVariantDangerGhost,
|
||||
Variant: IconButtonVariantDanger,
|
||||
Tone: IconButtonToneGhost,
|
||||
Type: "button",
|
||||
})
|
||||
|
||||
|
|
@ -50,6 +51,8 @@ func TestIconButtonRendersBorderlessDestructiveMarkup(t *testing.T) {
|
|||
`type="button"`,
|
||||
`aria-label="Supprimer le projet"`,
|
||||
`borderless-icon-button`,
|
||||
`ui-icon-button-ghost`,
|
||||
`ui-icon-button-danger`,
|
||||
`lucide-trash2`,
|
||||
} {
|
||||
if !strings.Contains(html, want) {
|
||||
|
|
@ -58,6 +61,31 @@ func TestIconButtonRendersBorderlessDestructiveMarkup(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestIconButtonRendersBorderlessNeutralMarkup(t *testing.T) {
|
||||
component := IconButton(IconButtonProps{
|
||||
Label: "Modifier le projet",
|
||||
Icon: "pencil",
|
||||
Variant: IconButtonVariantNeutral,
|
||||
Tone: IconButtonToneGhost,
|
||||
Type: "button",
|
||||
})
|
||||
|
||||
html := renderToString(t, component)
|
||||
|
||||
for _, want := range []string{
|
||||
`type="button"`,
|
||||
`aria-label="Modifier le projet"`,
|
||||
`borderless-icon-button`,
|
||||
`ui-icon-button-ghost`,
|
||||
`ui-icon-button-neutral`,
|
||||
`M12 20h9`,
|
||||
} {
|
||||
if !strings.Contains(html, want) {
|
||||
t.Fatalf("expected %q in %q", want, html)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBadgeRendersSemanticStatusVariant(t *testing.T) {
|
||||
component := Badge(BadgeProps{
|
||||
Label: "En cours",
|
||||
|
|
@ -79,8 +107,8 @@ func TestBadgeRendersSemanticStatusVariant(t *testing.T) {
|
|||
|
||||
func TestModalRendersShellStructure(t *testing.T) {
|
||||
component := Modal(ModalProps{
|
||||
Title: "Nouveau projet",
|
||||
Body: textComponent("Body copy"),
|
||||
Title: "Nouveau projet",
|
||||
Body: textComponent("Body copy"),
|
||||
Actions: textComponent("Actions"),
|
||||
})
|
||||
|
||||
|
|
@ -99,6 +127,38 @@ func TestModalRendersShellStructure(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestSpaceXRendersDefaultMediumMarkup(t *testing.T) {
|
||||
component := SpaceX(SpaceProps{})
|
||||
|
||||
html := renderToString(t, component)
|
||||
|
||||
for _, want := range []string{
|
||||
`aria-hidden="true"`,
|
||||
`ui-space-x`,
|
||||
`ui-space-x-md`,
|
||||
} {
|
||||
if !strings.Contains(html, want) {
|
||||
t.Fatalf("expected %q in %q", want, html)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSpaceYRendersExplicitExtraLargeMarkup(t *testing.T) {
|
||||
component := SpaceY(SpaceProps{Size: SpacingStepXL})
|
||||
|
||||
html := renderToString(t, component)
|
||||
|
||||
for _, want := range []string{
|
||||
`aria-hidden="true"`,
|
||||
`ui-space-y`,
|
||||
`ui-space-y-xl`,
|
||||
} {
|
||||
if !strings.Contains(html, want) {
|
||||
t.Fatalf("expected %q in %q", want, html)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestButtonUsesSharedTokenBackedClasses(t *testing.T) {
|
||||
component := Button(ButtonProps{
|
||||
Label: "Create",
|
||||
|
|
@ -134,7 +194,12 @@ func TestSharedSemanticClassesExistInStylesheet(t *testing.T) {
|
|||
`.ui-button-sm`,
|
||||
`.ui-badge-warning`,
|
||||
`.ui-modal-panel`,
|
||||
`.ui-space-x-md`,
|
||||
`.ui-space-y-md`,
|
||||
`.borderless-icon-button`,
|
||||
`.ui-icon-button-solid.ui-icon-button-neutral`,
|
||||
`.ui-icon-button-ghost.ui-icon-button-neutral`,
|
||||
`.ui-icon-button-ghost.ui-icon-button-danger`,
|
||||
`.ui-button-soft.ui-button-danger`,
|
||||
} {
|
||||
if !strings.Contains(css, want) {
|
||||
|
|
|
|||
|
|
@ -28,8 +28,27 @@ const (
|
|||
type IconButtonVariant string
|
||||
|
||||
const (
|
||||
IconButtonVariantNeutral IconButtonVariant = "neutral"
|
||||
IconButtonVariantDangerGhost IconButtonVariant = "danger-ghost"
|
||||
IconButtonVariantNeutral IconButtonVariant = "neutral"
|
||||
IconButtonVariantWarning IconButtonVariant = "warning"
|
||||
IconButtonVariantSuccess IconButtonVariant = "success"
|
||||
IconButtonVariantDanger IconButtonVariant = "danger"
|
||||
)
|
||||
|
||||
type IconButtonTone string
|
||||
|
||||
const (
|
||||
IconButtonToneSolid IconButtonTone = "solid"
|
||||
IconButtonToneGhost IconButtonTone = "ghost"
|
||||
)
|
||||
|
||||
type SpacingStep string
|
||||
|
||||
const (
|
||||
SpacingStepXS SpacingStep = "xs"
|
||||
SpacingStepSM SpacingStep = "sm"
|
||||
SpacingStepMD SpacingStep = "md"
|
||||
SpacingStepLG SpacingStep = "lg"
|
||||
SpacingStepXL SpacingStep = "xl"
|
||||
)
|
||||
|
||||
type BadgeVariant string
|
||||
|
|
@ -45,12 +64,14 @@ func buttonClass(variant ButtonVariant, tone ButtonTone, size Size) string {
|
|||
return "ui-button ui-button-" + string(normalizedButtonTone(tone)) + " ui-button-" + string(normalizedButtonVariant(variant)) + " ui-button-" + string(normalizedSize(size))
|
||||
}
|
||||
|
||||
func iconButtonClass(variant IconButtonVariant) string {
|
||||
switch variant {
|
||||
case IconButtonVariantDangerGhost:
|
||||
return "borderless-icon-button"
|
||||
func iconButtonClass(variant IconButtonVariant, tone IconButtonTone) string {
|
||||
normalizedVariant := normalizedIconButtonVariant(variant)
|
||||
|
||||
switch normalizedIconButtonTone(tone) {
|
||||
case IconButtonToneGhost:
|
||||
return "borderless-icon-button ui-icon-button-ghost ui-icon-button-" + string(normalizedVariant)
|
||||
default:
|
||||
return "ui-icon-button"
|
||||
return "ui-icon-button ui-icon-button-solid ui-icon-button-" + string(normalizedVariant)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -58,6 +79,14 @@ func badgeClass(variant BadgeVariant) string {
|
|||
return "ui-badge ui-badge-" + string(normalizedBadgeVariant(variant))
|
||||
}
|
||||
|
||||
func spaceXClass(step SpacingStep) string {
|
||||
return "ui-space-x ui-space-x-" + string(normalizedSpacingStep(step))
|
||||
}
|
||||
|
||||
func spaceYClass(step SpacingStep) string {
|
||||
return "ui-space-y ui-space-y-" + string(normalizedSpacingStep(step))
|
||||
}
|
||||
|
||||
func normalizedSize(size Size) Size {
|
||||
switch size {
|
||||
case SizeSM, SizeLG:
|
||||
|
|
@ -85,6 +114,33 @@ func normalizedButtonTone(tone ButtonTone) ButtonTone {
|
|||
}
|
||||
}
|
||||
|
||||
func normalizedIconButtonVariant(variant IconButtonVariant) IconButtonVariant {
|
||||
switch variant {
|
||||
case IconButtonVariantWarning, IconButtonVariantSuccess, IconButtonVariantDanger:
|
||||
return variant
|
||||
default:
|
||||
return IconButtonVariantNeutral
|
||||
}
|
||||
}
|
||||
|
||||
func normalizedIconButtonTone(tone IconButtonTone) IconButtonTone {
|
||||
switch tone {
|
||||
case IconButtonToneGhost:
|
||||
return tone
|
||||
default:
|
||||
return IconButtonToneSolid
|
||||
}
|
||||
}
|
||||
|
||||
func normalizedSpacingStep(step SpacingStep) SpacingStep {
|
||||
switch step {
|
||||
case SpacingStepXS, SpacingStepSM, SpacingStepLG, SpacingStepXL:
|
||||
return step
|
||||
default:
|
||||
return SpacingStepMD
|
||||
}
|
||||
}
|
||||
|
||||
func normalizedBadgeVariant(variant BadgeVariant) BadgeVariant {
|
||||
switch variant {
|
||||
case BadgeVariantWarning, BadgeVariantSuccess, BadgeVariantDanger:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package views
|
||||
|
||||
import "strconv"
|
||||
import "xtablo-backend/internal/web/ui"
|
||||
|
||||
templ DashboardPage(activePath string, content templ.Component) {
|
||||
@DashboardPageWithMainClass(activePath, "dashboard-main flex-1 overflow-auto", content)
|
||||
|
|
@ -193,7 +194,13 @@ templ OverviewHeader(displayName string) {
|
|||
<div class="overview-header-actions">
|
||||
<span class="overview-badge">Founder</span>
|
||||
<form action="/logout" method="post" class="overview-logout-form">
|
||||
<button type="submit" class="overview-logout-button">Se déconnecter</button>
|
||||
@ui.Button(ui.ButtonProps{
|
||||
Label: "Se déconnecter",
|
||||
Variant: ui.ButtonVariantDanger,
|
||||
Tone: ui.ButtonToneSoft,
|
||||
Size: ui.SizeMD,
|
||||
Type: "submit",
|
||||
})
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -220,7 +227,7 @@ templ OverviewProjectsSection(projects []TabloCardView) {
|
|||
for _, project := range hiddenOverviewProjects(projects) {
|
||||
@TabloGridCardWithAttrs(project, templ.Attributes{
|
||||
"data-overview-project-hidden": "true",
|
||||
"hidden": true,
|
||||
"hidden": true,
|
||||
})
|
||||
}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import "github.com/a-h/templ"
|
|||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import "strconv"
|
||||
import "xtablo-backend/internal/web/ui"
|
||||
|
||||
func DashboardPage(activePath string, content templ.Component) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
|
|
@ -669,7 +670,7 @@ func AppSectionMainContent(title string, description string) templ.Component {
|
|||
var templ_7745c5c3_Var21 string
|
||||
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(title)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 161, Col: 14}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 162, Col: 14}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
|
|
@ -682,7 +683,7 @@ func AppSectionMainContent(title string, description string) templ.Component {
|
|||
var templ_7745c5c3_Var22 string
|
||||
templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(description)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 162, Col: 19}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 163, Col: 19}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
|
|
@ -724,7 +725,7 @@ func NotFoundContent(displayName string) templ.Component {
|
|||
var templ_7745c5c3_Var24 string
|
||||
templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(dashboardGreetingName(displayName))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 182, Col: 48}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 183, Col: 48}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
|
|
@ -766,7 +767,7 @@ func OverviewHeader(displayName string) templ.Component {
|
|||
var templ_7745c5c3_Var26 string
|
||||
templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(dashboardTodayLabel())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 190, Col: 50}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 191, Col: 50}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
|
|
@ -779,13 +780,27 @@ func OverviewHeader(displayName string) templ.Component {
|
|||
var templ_7745c5c3_Var27 string
|
||||
templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(dashboardGreetingName(displayName))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 192, Col: 84}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 193, Col: 84}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "</span>!</h2><div class=\"overview-header-actions\"><span class=\"overview-badge\">Founder</span><form action=\"/logout\" method=\"post\" class=\"overview-logout-form\"><button type=\"submit\" class=\"overview-logout-button\">Se déconnecter</button></form></div></div></header>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "</span>!</h2><div class=\"overview-header-actions\"><span class=\"overview-badge\">Founder</span><form action=\"/logout\" method=\"post\" class=\"overview-logout-form\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = ui.Button(ui.ButtonProps{
|
||||
Label: "Se déconnecter",
|
||||
Variant: ui.ButtonVariantDanger,
|
||||
Tone: ui.ButtonToneSoft,
|
||||
Size: ui.SizeMD,
|
||||
Type: "submit",
|
||||
}).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "</form></div></div></header>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
@ -814,7 +829,7 @@ func OverviewActions(actions []quickAction) templ.Component {
|
|||
templ_7745c5c3_Var28 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "<section class=\"overview-actions\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "<section class=\"overview-actions\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
@ -824,7 +839,7 @@ func OverviewActions(actions []quickAction) templ.Component {
|
|||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "</section>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "</section>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
@ -853,7 +868,7 @@ func OverviewProjectsSection(projects []TabloCardView) templ.Component {
|
|||
templ_7745c5c3_Var29 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "<section id=\"overview-projects-section\" class=\"overview-section\"><div class=\"overview-section-heading\"><h3>Mes Projets</h3></div><div class=\"project-grid\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "<section id=\"overview-projects-section\" class=\"overview-section\"><div class=\"overview-section-heading\"><h3>Mes Projets</h3></div><div class=\"project-grid\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
@ -872,7 +887,7 @@ func OverviewProjectsSection(projects []TabloCardView) templ.Component {
|
|||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "</div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
@ -880,7 +895,7 @@ func OverviewProjectsSection(projects []TabloCardView) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "</section>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "</section>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
@ -914,33 +929,33 @@ func SeeMoreProjects(hiddenCount int) templ.Component {
|
|||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
if hiddenCount > 0 {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "<div class=\"overview-more-row\"><button type=\"button\" class=\"overview-more-button\" data-overview-see-more=\"true\" data-overview-expanded=\"false\" data-overview-hidden-count=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "<div class=\"overview-more-row\"><button type=\"button\" class=\"overview-more-button\" data-overview-see-more=\"true\" data-overview-expanded=\"false\" data-overview-hidden-count=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var31 string
|
||||
templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(hiddenCount))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 240, Col: 58}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 247, Col: 58}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "\" data-overview-hide-label=\"Masquer\" data-overview-chevron-down=\"m6 9 6 6 6-6\" data-overview-chevron-up=\"m6 15 6-6 6 6\"><span data-overview-see-more-label=\"true\">Voir ")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "\" data-overview-hide-label=\"Masquer\" data-overview-chevron-down=\"m6 9 6 6 6-6\" data-overview-chevron-up=\"m6 15 6-6 6 6\"><span data-overview-see-more-label=\"true\">Voir ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var32 string
|
||||
templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(hiddenCount)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 245, Col: 64}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 252, Col: 64}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, " de plus</span> <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path data-overview-see-more-chevron=\"true\" d=\"m6 9 6 6 6-6\"></path></svg></button></div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, " de plus</span> <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path data-overview-see-more-chevron=\"true\" d=\"m6 9 6 6 6-6\"></path></svg></button></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
@ -970,7 +985,7 @@ func OverviewProjectsScript() templ.Component {
|
|||
templ_7745c5c3_Var33 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "<script>\n\t\t(function () {\n\t\t\tif (window.xtabloOverviewProjectsInitialized) return;\n\t\t\tdocument.addEventListener(\"click\", function (event) {\n\t\t\t\tvar button = event.target.closest(\"[data-overview-see-more]\");\n\t\t\t\tif (!button) return;\n\t\t\t\tvar section = button.closest(\"#overview-projects-section\");\n\t\t\t\tif (!section) return;\n\t\t\t\tvar expanded = button.getAttribute(\"data-overview-expanded\") === \"true\";\n\t\t\t\tvar label = button.querySelector(\"[data-overview-see-more-label]\");\n\t\t\t\tvar chevron = button.querySelector(\"[data-overview-see-more-chevron]\");\n\t\t\t\tvar hiddenCount = button.getAttribute(\"data-overview-hidden-count\");\n\t\t\t\tvar hideLabel = button.getAttribute(\"data-overview-hide-label\") || \"Masquer\";\n\t\t\t\tvar downChevron = button.getAttribute(\"data-overview-chevron-down\") || \"m6 9 6 6 6-6\";\n\t\t\t\tvar upChevron = button.getAttribute(\"data-overview-chevron-up\") || \"m6 15 6-6 6 6\";\n\t\t\t\tsection.querySelectorAll(\"[data-overview-project-hidden=\\\"true\\\"]\").forEach(function (project) {\n\t\t\t\t\tproject.hidden = expanded;\n\t\t\t\t\tif (expanded) {\n\t\t\t\t\t\tproject.setAttribute(\"hidden\", \"\");\n\t\t\t\t\t} else {\n\t\t\t\t\t\tproject.removeAttribute(\"hidden\");\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tbutton.setAttribute(\"data-overview-expanded\", expanded ? \"false\" : \"true\");\n\t\t\t\tif (label) {\n\t\t\t\t\tlabel.textContent = expanded ? \"Voir \" + hiddenCount + \" de plus\" : hideLabel;\n\t\t\t\t}\n\t\t\t\tif (chevron) {\n\t\t\t\t\tchevron.setAttribute(\"d\", expanded ? downChevron : upChevron);\n\t\t\t\t}\n\t\t\t});\n\t\t\twindow.xtabloOverviewProjectsInitialized = true;\n\t\t})();\n\t</script>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "<script>\n\t\t(function () {\n\t\t\tif (window.xtabloOverviewProjectsInitialized) return;\n\t\t\tdocument.addEventListener(\"click\", function (event) {\n\t\t\t\tvar button = event.target.closest(\"[data-overview-see-more]\");\n\t\t\t\tif (!button) return;\n\t\t\t\tvar section = button.closest(\"#overview-projects-section\");\n\t\t\t\tif (!section) return;\n\t\t\t\tvar expanded = button.getAttribute(\"data-overview-expanded\") === \"true\";\n\t\t\t\tvar label = button.querySelector(\"[data-overview-see-more-label]\");\n\t\t\t\tvar chevron = button.querySelector(\"[data-overview-see-more-chevron]\");\n\t\t\t\tvar hiddenCount = button.getAttribute(\"data-overview-hidden-count\");\n\t\t\t\tvar hideLabel = button.getAttribute(\"data-overview-hide-label\") || \"Masquer\";\n\t\t\t\tvar downChevron = button.getAttribute(\"data-overview-chevron-down\") || \"m6 9 6 6 6-6\";\n\t\t\t\tvar upChevron = button.getAttribute(\"data-overview-chevron-up\") || \"m6 15 6-6 6 6\";\n\t\t\t\tsection.querySelectorAll(\"[data-overview-project-hidden=\\\"true\\\"]\").forEach(function (project) {\n\t\t\t\t\tproject.hidden = expanded;\n\t\t\t\t\tif (expanded) {\n\t\t\t\t\t\tproject.setAttribute(\"hidden\", \"\");\n\t\t\t\t\t} else {\n\t\t\t\t\t\tproject.removeAttribute(\"hidden\");\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tbutton.setAttribute(\"data-overview-expanded\", expanded ? \"false\" : \"true\");\n\t\t\t\tif (label) {\n\t\t\t\t\tlabel.textContent = expanded ? \"Voir \" + hiddenCount + \" de plus\" : hideLabel;\n\t\t\t\t}\n\t\t\t\tif (chevron) {\n\t\t\t\t\tchevron.setAttribute(\"d\", expanded ? downChevron : upChevron);\n\t\t\t\t}\n\t\t\t});\n\t\t\twindow.xtabloOverviewProjectsInitialized = true;\n\t\t})();\n\t</script>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
@ -999,7 +1014,7 @@ func OverviewTasks(tasks []dashboardTask) templ.Component {
|
|||
templ_7745c5c3_Var34 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "<section class=\"overview-section tasks-section\"><div class=\"tasks-section-header\"><h3>Mes Tâches</h3><button type=\"button\" class=\"tasks-add-button\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M5 12h14\"></path> <path d=\"M12 5v14\"></path></svg> <span>Ajouter</span></button></div><div class=\"task-list\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "<section class=\"overview-section tasks-section\"><div class=\"tasks-section-header\"><h3>Mes Tâches</h3><button type=\"button\" class=\"tasks-add-button\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M5 12h14\"></path> <path d=\"M12 5v14\"></path></svg> <span>Ajouter</span></button></div><div class=\"task-list\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
@ -1009,7 +1024,7 @@ func OverviewTasks(tasks []dashboardTask) templ.Component {
|
|||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "</div></section>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "</div></section>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
@ -1038,7 +1053,7 @@ func QuickActionCard(action quickAction) templ.Component {
|
|||
templ_7745c5c3_Var35 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "<button class=\"quick-action-card\" type=\"button\"><div class=\"quick-action-icon\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "<button class=\"quick-action-card\" type=\"button\"><div class=\"quick-action-icon\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
@ -1046,33 +1061,33 @@ func QuickActionCard(action quickAction) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "</div><div class=\"quick-action-copy\"><div class=\"quick-action-title\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "</div><div class=\"quick-action-copy\"><div class=\"quick-action-title\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var36 string
|
||||
templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.JoinStringErrs(action.Title)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 317, Col: 49}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 324, Col: 49}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var36))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "</div><p>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "</div><p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var37 string
|
||||
templ_7745c5c3_Var37, templ_7745c5c3_Err = templ.JoinStringErrs(action.Description)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 318, Col: 26}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 325, Col: 26}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var37))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "</p></div></button>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "</p></div></button>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
@ -1106,7 +1121,7 @@ func TaskRow(task dashboardTask) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "<div class=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "<div class=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
@ -1119,7 +1134,7 @@ func TaskRow(task dashboardTask) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
@ -1128,7 +1143,7 @@ func TaskRow(task dashboardTask) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "<button class=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "<button class=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
@ -1141,7 +1156,7 @@ func TaskRow(task dashboardTask) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "\" type=\"button\" aria-label=\"Marquer la tâche\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "\" type=\"button\" aria-label=\"Marquer la tâche\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
@ -1151,20 +1166,20 @@ func TaskRow(task dashboardTask) templ.Component {
|
|||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "</button><div class=\"task-body\"><p>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "</button><div class=\"task-body\"><p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var43 string
|
||||
templ_7745c5c3_Var43, templ_7745c5c3_Err = templ.JoinStringErrs(task.Title)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 331, Col: 18}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 338, Col: 18}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var43))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "</p><div class=\"task-meta\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "</p><div class=\"task-meta\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
@ -1173,7 +1188,7 @@ func TaskRow(task dashboardTask) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "<div class=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "<div class=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
@ -1186,46 +1201,46 @@ func TaskRow(task dashboardTask) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "\"><span>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "\"><span>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var46 string
|
||||
templ_7745c5c3_Var46, templ_7745c5c3_Err = templ.JoinStringErrs(task.ProjectKey)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 334, Col: 28}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 341, Col: 28}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var46))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "</span></div><span class=\"task-project-name\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "</span></div><span class=\"task-project-name\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var47 string
|
||||
templ_7745c5c3_Var47, templ_7745c5c3_Err = templ.JoinStringErrs(task.Project)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 336, Col: 50}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 343, Col: 50}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var47))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "</span> <span class=\"task-date\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "</span> <span class=\"task-date\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var48 string
|
||||
templ_7745c5c3_Var48, templ_7745c5c3_Err = templ.JoinStringErrs(task.Date)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 337, Col: 39}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 344, Col: 39}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var48))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "</span></div></div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "</span></div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
@ -1234,7 +1249,7 @@ func TaskRow(task dashboardTask) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "<span class=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "<span class=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
@ -1247,20 +1262,20 @@ func TaskRow(task dashboardTask) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var51 string
|
||||
templ_7745c5c3_Var51, templ_7745c5c3_Err = templ.JoinStringErrs(task.Status)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 340, Col: 75}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 347, Col: 75}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var51))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "</span></div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "</span></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
@ -1294,20 +1309,20 @@ func SidebarNavItem(item sidebarNavItem) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "<div id=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "<div id=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var54 string
|
||||
templ_7745c5c3_Var54, templ_7745c5c3_Err = templ.JoinStringErrs(sidebarNavItemID(item.Href))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 345, Col: 38}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 352, Col: 38}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var54))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "\" class=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, "\" class=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
@ -1320,33 +1335,33 @@ func SidebarNavItem(item sidebarNavItem) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, "\"><a class=\"sidebar-nav-link\" href=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, "\"><a class=\"sidebar-nav-link\" href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var56 templ.SafeURL
|
||||
templ_7745c5c3_Var56, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(item.Href))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 346, Col: 61}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 353, Col: 61}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var56))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, "\" hx-get=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, "\" hx-get=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var57 string
|
||||
templ_7745c5c3_Var57, templ_7745c5c3_Err = templ.JoinStringErrs(item.Href)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 346, Col: 82}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 353, Col: 82}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var57))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, "\" hx-target=\"#app-main-content\" hx-swap=\"outerHTML\" hx-push-url=\"true\"><div class=\"sidebar-nav-link-inner\"><span class=\"sidebar-nav-icon\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, "\" hx-target=\"#app-main-content\" hx-swap=\"outerHTML\" hx-push-url=\"true\"><div class=\"sidebar-nav-link-inner\"><span class=\"sidebar-nav-icon\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
@ -1354,20 +1369,20 @@ func SidebarNavItem(item sidebarNavItem) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, "</span><div class=\"sidebar-nav-label\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, "</span><div class=\"sidebar-nav-label\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var58 string
|
||||
templ_7745c5c3_Var58, templ_7745c5c3_Err = templ.JoinStringErrs(item.Label)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 351, Col: 47}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 358, Col: 47}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var58))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, "</div></div></a></div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 65, "</div></div></a></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
@ -1401,20 +1416,20 @@ func SidebarNavItemOOB(item sidebarNavItem) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 65, "<div id=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 66, "<div id=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var61 string
|
||||
templ_7745c5c3_Var61, templ_7745c5c3_Err = templ.JoinStringErrs(sidebarNavItemID(item.Href))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 358, Col: 38}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 365, Col: 38}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var61))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 66, "\" class=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 67, "\" class=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
@ -1427,33 +1442,33 @@ func SidebarNavItemOOB(item sidebarNavItem) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 67, "\" hx-swap-oob=\"outerHTML\"><a class=\"sidebar-nav-link\" href=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 68, "\" hx-swap-oob=\"outerHTML\"><a class=\"sidebar-nav-link\" href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var63 templ.SafeURL
|
||||
templ_7745c5c3_Var63, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(item.Href))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 359, Col: 61}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 366, Col: 61}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var63))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 68, "\" hx-get=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 69, "\" hx-get=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var64 string
|
||||
templ_7745c5c3_Var64, templ_7745c5c3_Err = templ.JoinStringErrs(item.Href)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 359, Col: 82}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 366, Col: 82}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var64))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 69, "\" hx-target=\"#app-main-content\" hx-swap=\"outerHTML\" hx-push-url=\"true\"><div class=\"sidebar-nav-link-inner\"><span class=\"sidebar-nav-icon\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 70, "\" hx-target=\"#app-main-content\" hx-swap=\"outerHTML\" hx-push-url=\"true\"><div class=\"sidebar-nav-link-inner\"><span class=\"sidebar-nav-icon\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
@ -1461,20 +1476,20 @@ func SidebarNavItemOOB(item sidebarNavItem) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 70, "</span><div class=\"sidebar-nav-label\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, "</span><div class=\"sidebar-nav-label\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var65 string
|
||||
templ_7745c5c3_Var65, templ_7745c5c3_Err = templ.JoinStringErrs(item.Label)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 364, Col: 47}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 371, Col: 47}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var65))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, "</div></div></a></div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 72, "</div></div></a></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
@ -1503,20 +1518,20 @@ func SidebarProjectItem(item sidebarProjectItem) templ.Component {
|
|||
templ_7745c5c3_Var66 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 72, "<a class=\"sidebar-project-link\" href=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 73, "<a class=\"sidebar-project-link\" href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var67 templ.SafeURL
|
||||
templ_7745c5c3_Var67, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(item.Href))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 371, Col: 64}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 378, Col: 64}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var67))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 73, "\"><span class=\"sidebar-project-icon\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 74, "\"><span class=\"sidebar-project-icon\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
@ -1524,20 +1539,20 @@ func SidebarProjectItem(item sidebarProjectItem) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 74, "</span> <span class=\"sidebar-project-label\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 75, "</span> <span class=\"sidebar-project-label\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var68 string
|
||||
templ_7745c5c3_Var68, templ_7745c5c3_Err = templ.JoinStringErrs(item.Label)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 375, Col: 50}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 382, Col: 50}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var68))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 75, "</span></a>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 76, "</span></a>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -113,6 +113,7 @@ func OverviewProjectsFromTablos(tablos []tablomodel.Record) []TabloCardView {
|
|||
projects = append(projects, TabloCardView{
|
||||
ID: tablo.ID.String(),
|
||||
Name: tablo.Name,
|
||||
Color: strings.TrimSpace(tablo.Color),
|
||||
Status: string(tablo.Status),
|
||||
StatusLabel: statusLabel,
|
||||
StatusTone: statusTone,
|
||||
|
|
@ -122,6 +123,7 @@ func OverviewProjectsFromTablos(tablos []tablomodel.Record) []TabloCardView {
|
|||
Progress: progress,
|
||||
ProgressLabel: progressPercentLabel(progress),
|
||||
DeleteRequestURL: "/tablos/" + tablo.ID.String(),
|
||||
EditRequestURL: "/tablos/" + tablo.ID.String() + "/edit",
|
||||
})
|
||||
}
|
||||
return projects
|
||||
|
|
|
|||
|
|
@ -112,8 +112,12 @@ templ TablosPageContent(vm TablosPageViewModel) {
|
|||
}),
|
||||
})
|
||||
}
|
||||
if vm.ModalOpen {
|
||||
@CreateTabloModal(vm)
|
||||
if vm.HasModal() {
|
||||
if vm.IsCreateModal() {
|
||||
@CreateTabloModal(vm)
|
||||
} else if vm.IsEditModal() {
|
||||
@EditTabloModal(vm)
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
|
@ -140,7 +144,8 @@ templ BorderlessDeleteButton(deleteRequestURL string) {
|
|||
@ui.IconButton(ui.IconButtonProps{
|
||||
Label: "Supprimer le projet",
|
||||
Icon: "trash",
|
||||
Variant: ui.IconButtonVariantDangerGhost,
|
||||
Variant: ui.IconButtonVariantDanger,
|
||||
Tone: ui.IconButtonToneGhost,
|
||||
Type: "button",
|
||||
Attrs: templ.Attributes{
|
||||
"hx-delete": deleteRequestURL,
|
||||
|
|
@ -151,21 +156,40 @@ templ BorderlessDeleteButton(deleteRequestURL string) {
|
|||
})
|
||||
}
|
||||
|
||||
templ EditTabloButton(editRequestURL string) {
|
||||
@ui.IconButton(ui.IconButtonProps{
|
||||
Label: "Modifier le projet",
|
||||
Icon: "pencil",
|
||||
Variant: ui.IconButtonVariantNeutral,
|
||||
Tone: ui.IconButtonToneGhost,
|
||||
Type: "button",
|
||||
Attrs: templ.Attributes{
|
||||
"hx-get": editRequestURL,
|
||||
"hx-target": "#app-main-content",
|
||||
"hx-swap": "outerHTML",
|
||||
"hx-push-url": "true",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
templ TabloGridCard(tablo TabloCardView) {
|
||||
@TabloGridCardWithAttrs(tablo, nil)
|
||||
}
|
||||
|
||||
templ TabloGridCardWithAttrs(tablo TabloCardView, attrs templ.Attributes) {
|
||||
<article class="project-card" { attrs... }>
|
||||
<article class="project-card" style={ projectColorVariableStyle(tablo.Color) } { attrs... }>
|
||||
<div class="project-card-top">
|
||||
@ui.Badge(ui.BadgeProps{
|
||||
Label: tablo.StatusLabel,
|
||||
Variant: badgeVariantForTone(tablo.StatusTone),
|
||||
})
|
||||
@BorderlessDeleteButton(tablo.DeleteRequestURL)
|
||||
<div class="flex items-center gap-2">
|
||||
@EditTabloButton(tablo.EditRequestURL)
|
||||
@BorderlessDeleteButton(tablo.DeleteRequestURL)
|
||||
</div>
|
||||
</div>
|
||||
<div class="project-card-title-row">
|
||||
<div class={ "project-avatar " + projectAccentClass(tablo.Accent) }>
|
||||
<div class="project-avatar">
|
||||
<span>{ tablo.Initial }</span>
|
||||
</div>
|
||||
<h4>{ tablo.Name }</h4>
|
||||
|
|
@ -180,17 +204,17 @@ templ TabloGridCardWithAttrs(tablo TabloCardView, attrs templ.Attributes) {
|
|||
<strong>{ tablo.ProgressLabel }</strong>
|
||||
</div>
|
||||
<div class="project-progress-track">
|
||||
<div class={ "project-progress-bar " + projectAccentClass(tablo.Accent) } style={ progressInlineStyle(tablo.Progress) }></div>
|
||||
<div class="project-progress-bar" 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">
|
||||
<tr class="border-t border-[#EAECF0] dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors cursor-pointer" style={ projectColorVariableStyle(tablo.Color) }>
|
||||
<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 }>
|
||||
<div class="project-list-icon w-8 h-8 rounded-lg flex items-center justify-center shrink-0 overflow-hidden [&>svg]:w-4 [&>svg]:h-4">
|
||||
@ActionIcon(tablo.IconKind)
|
||||
</div>
|
||||
<span class="font-medium text-gray-900 dark:text-gray-100 truncate">{ tablo.Name }</span>
|
||||
|
|
@ -211,13 +235,16 @@ templ TabloListRow(tablo TabloCardView) {
|
|||
<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 class="project-progress-bar 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)
|
||||
<div class="flex items-center justify-end gap-1">
|
||||
@EditTabloButton(tablo.EditRequestURL)
|
||||
@BorderlessDeleteButton(tablo.DeleteRequestURL)
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
|
|
@ -269,7 +296,21 @@ templ CreateTabloModalBody(vm TablosPageViewModel) {
|
|||
Placeholder: "Nom du projet",
|
||||
Type: "text",
|
||||
}),
|
||||
Error: vm.ErrorMessage,
|
||||
})
|
||||
@ui.FormField(ui.FormFieldProps{
|
||||
Label: "Couleur",
|
||||
For: "tablo-color",
|
||||
Field: ui.Input(ui.InputProps{
|
||||
ID: "tablo-color",
|
||||
Name: "color",
|
||||
Value: vm.FormColor,
|
||||
Placeholder: "#3B82F6",
|
||||
Type: "text",
|
||||
Attrs: templ.Attributes{
|
||||
"pattern": "^#[0-9A-Fa-f]{6}$",
|
||||
"autocomplete": "off",
|
||||
},
|
||||
}),
|
||||
})
|
||||
<div class="flex items-center justify-end gap-3">
|
||||
<a
|
||||
|
|
@ -291,3 +332,85 @@ templ CreateTabloModalBody(vm TablosPageViewModel) {
|
|||
</div>
|
||||
</form>
|
||||
}
|
||||
|
||||
templ EditTabloModal(vm TablosPageViewModel) {
|
||||
@ui.Modal(ui.ModalProps{
|
||||
Title: "Modifier le projet",
|
||||
Body: EditTabloModalBody(vm),
|
||||
})
|
||||
}
|
||||
|
||||
templ EditTabloColorField(vm TablosPageViewModel) {
|
||||
<div class="flex items-center gap-3">
|
||||
@ui.Input(ui.InputProps{
|
||||
ID: "edit-tablo-color",
|
||||
Name: "color",
|
||||
Value: vm.FormColor,
|
||||
Placeholder: "#3B82F6",
|
||||
Type: "text",
|
||||
Attrs: templ.Attributes{
|
||||
"pattern": "^#[0-9A-Fa-f]{6}$",
|
||||
"autocomplete": "off",
|
||||
"oninput": "document.getElementById('edit-tablo-color-picker').value=this.value",
|
||||
},
|
||||
})
|
||||
<input
|
||||
id="edit-tablo-color-picker"
|
||||
type="color"
|
||||
value={ vm.FormColor }
|
||||
class="ui-input tablo-color-picker"
|
||||
oninput="document.getElementById('edit-tablo-color').value=this.value"
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
|
||||
templ EditTabloModalBody(vm TablosPageViewModel) {
|
||||
<form
|
||||
hx-post={ vm.EditSubmitHref() }
|
||||
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 }/>
|
||||
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: "edit-tablo-name",
|
||||
Field: ui.Input(ui.InputProps{
|
||||
ID: "edit-tablo-name",
|
||||
Name: "name",
|
||||
Value: vm.FormName,
|
||||
Placeholder: "Nom du projet",
|
||||
Type: "text",
|
||||
}),
|
||||
})
|
||||
@ui.FormField(ui.FormFieldProps{
|
||||
Label: "Couleur",
|
||||
For: "edit-tablo-color",
|
||||
Field: EditTabloColorField(vm),
|
||||
Hint: "Utilisez le champ hex ou le sélecteur pour choisir une couleur au format #RRGGBB.",
|
||||
})
|
||||
<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: "Enregistrer",
|
||||
Variant: ui.ButtonVariantDefault,
|
||||
Size: ui.SizeMD,
|
||||
Type: "submit",
|
||||
})
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -303,10 +303,17 @@ func TablosPageContent(vm TablosPageViewModel) templ.Component {
|
|||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
if vm.ModalOpen {
|
||||
templ_7745c5c3_Err = CreateTabloModal(vm).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
if vm.HasModal() {
|
||||
if vm.IsCreateModal() {
|
||||
templ_7745c5c3_Err = CreateTabloModal(vm).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else if vm.IsEditModal() {
|
||||
templ_7745c5c3_Err = EditTabloModal(vm).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "</div>")
|
||||
|
|
@ -350,7 +357,7 @@ func StatusPill(vm TablosPageViewModel, status string, label string) templ.Compo
|
|||
var templ_7745c5c3_Var16 templ.SafeURL
|
||||
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(vm.StatusHref(status)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 123, Col: 45}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 127, Col: 45}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
|
|
@ -363,7 +370,7 @@ func StatusPill(vm TablosPageViewModel, status string, label string) templ.Compo
|
|||
var templ_7745c5c3_Var17 string
|
||||
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(vm.StatusHref(status))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 124, Col: 32}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 128, Col: 32}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
|
|
@ -403,7 +410,7 @@ func StatusPill(vm TablosPageViewModel, status string, label string) templ.Compo
|
|||
var templ_7745c5c3_Var19 string
|
||||
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(label)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 135, Col: 9}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 139, Col: 9}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
|
|
@ -441,7 +448,8 @@ func BorderlessDeleteButton(deleteRequestURL string) templ.Component {
|
|||
templ_7745c5c3_Err = ui.IconButton(ui.IconButtonProps{
|
||||
Label: "Supprimer le projet",
|
||||
Icon: "trash",
|
||||
Variant: ui.IconButtonVariantDangerGhost,
|
||||
Variant: ui.IconButtonVariantDanger,
|
||||
Tone: ui.IconButtonToneGhost,
|
||||
Type: "button",
|
||||
Attrs: templ.Attributes{
|
||||
"hx-delete": deleteRequestURL,
|
||||
|
|
@ -457,6 +465,47 @@ func BorderlessDeleteButton(deleteRequestURL string) templ.Component {
|
|||
})
|
||||
}
|
||||
|
||||
func EditTabloButton(editRequestURL string) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var21 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var21 == nil {
|
||||
templ_7745c5c3_Var21 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = ui.IconButton(ui.IconButtonProps{
|
||||
Label: "Modifier le projet",
|
||||
Icon: "pencil",
|
||||
Variant: ui.IconButtonVariantNeutral,
|
||||
Tone: ui.IconButtonToneGhost,
|
||||
Type: "button",
|
||||
Attrs: templ.Attributes{
|
||||
"hx-get": editRequestURL,
|
||||
"hx-target": "#app-main-content",
|
||||
"hx-swap": "outerHTML",
|
||||
"hx-push-url": "true",
|
||||
},
|
||||
}).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func TabloGridCard(tablo TabloCardView) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
|
|
@ -473,9 +522,9 @@ func TabloGridCard(tablo TabloCardView) templ.Component {
|
|||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var21 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var21 == nil {
|
||||
templ_7745c5c3_Var21 = templ.NopComponent
|
||||
templ_7745c5c3_Var22 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var22 == nil {
|
||||
templ_7745c5c3_Var22 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = TabloGridCardWithAttrs(tablo, nil).Render(ctx, templ_7745c5c3_Buffer)
|
||||
|
|
@ -502,12 +551,25 @@ func TabloGridCardWithAttrs(tablo TabloCardView, attrs templ.Attributes) templ.C
|
|||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var22 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var22 == nil {
|
||||
templ_7745c5c3_Var22 = templ.NopComponent
|
||||
templ_7745c5c3_Var23 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var23 == nil {
|
||||
templ_7745c5c3_Var23 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "<article class=\"project-card\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "<article class=\"project-card\" style=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var24 string
|
||||
templ_7745c5c3_Var24, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(projectColorVariableStyle(tablo.Color))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 180, Col: 77}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
@ -515,7 +577,7 @@ func TabloGridCardWithAttrs(tablo TabloCardView, attrs templ.Attributes) templ.C
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "><div class=\"project-card-top\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "><div class=\"project-card-top\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
@ -526,40 +588,26 @@ func TabloGridCardWithAttrs(tablo TabloCardView, attrs templ.Attributes) templ.C
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "<div class=\"flex items-center gap-2\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = EditTabloButton(tablo.EditRequestURL).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = BorderlessDeleteButton(tablo.DeleteRequestURL).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "</div><div class=\"project-card-title-row\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var23 = []any{"project-avatar " + projectAccentClass(tablo.Accent)}
|
||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var23...)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "<div class=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var24 string
|
||||
templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var23).String())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 1, Col: 0}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "\"><span>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "</div></div><div class=\"project-card-title-row\"><div class=\"project-avatar\"><span>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var25 string
|
||||
templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(tablo.Initial)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 169, Col: 25}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 193, Col: 25}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
|
|
@ -572,7 +620,7 @@ func TabloGridCardWithAttrs(tablo TabloCardView, attrs templ.Attributes) templ.C
|
|||
var templ_7745c5c3_Var26 string
|
||||
templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(tablo.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 171, Col: 19}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 195, Col: 19}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
|
|
@ -593,7 +641,7 @@ func TabloGridCardWithAttrs(tablo TabloCardView, attrs templ.Attributes) templ.C
|
|||
var templ_7745c5c3_Var27 string
|
||||
templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(tablo.CardDateLabel)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 175, Col: 30}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 199, Col: 30}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
|
|
@ -606,48 +654,26 @@ func TabloGridCardWithAttrs(tablo TabloCardView, attrs templ.Attributes) templ.C
|
|||
var templ_7745c5c3_Var28 string
|
||||
templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(tablo.ProgressLabel)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 180, Col: 33}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 204, Col: 33}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "</strong></div><div class=\"project-progress-track\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "</strong></div><div class=\"project-progress-track\"><div class=\"project-progress-bar\" style=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var29 = []any{"project-progress-bar " + projectAccentClass(tablo.Accent)}
|
||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var29...)
|
||||
var templ_7745c5c3_Var29 string
|
||||
templ_7745c5c3_Var29, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(progressInlineStyle(tablo.Progress))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 207, Col: 81}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "<div class=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var30 string
|
||||
templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var29).String())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 1, Col: 0}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "\" style=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var31 string
|
||||
templ_7745c5c3_Var31, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(progressInlineStyle(tablo.Progress))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 183, Col: 121}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "\"></div></div></div></article>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "\"></div></div></div></article>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
@ -671,34 +697,25 @@ func TabloListRow(tablo TabloCardView) templ.Component {
|
|||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var32 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var32 == nil {
|
||||
templ_7745c5c3_Var32 = templ.NopComponent
|
||||
templ_7745c5c3_Var30 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var30 == nil {
|
||||
templ_7745c5c3_Var30 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "<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\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "<tr class=\"border-t border-[#EAECF0] dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors cursor-pointer\" style=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var33 = []any{"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}
|
||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var33...)
|
||||
var templ_7745c5c3_Var31 string
|
||||
templ_7745c5c3_Var31, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(projectColorVariableStyle(tablo.Color))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 214, Col: 179}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "<div class=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var34 string
|
||||
templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var33).String())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 1, Col: 0}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var34))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "\"><td class=\"px-6 py-4\"><div class=\"flex items-center gap-3\"><div class=\"project-list-icon w-8 h-8 rounded-lg flex items-center justify-center shrink-0 overflow-hidden [&>svg]:w-4 [&>svg]:h-4\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
@ -706,20 +723,20 @@ func TabloListRow(tablo TabloCardView) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "</div><span class=\"font-medium text-gray-900 dark:text-gray-100 truncate\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "</div><span class=\"font-medium text-gray-900 dark:text-gray-100 truncate\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var35 string
|
||||
templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.JoinStringErrs(tablo.Name)
|
||||
var templ_7745c5c3_Var32 string
|
||||
templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(tablo.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 196, Col: 84}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 220, Col: 84}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var35))
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "</span></div></td><td class=\"px-6 py-4 whitespace-nowrap\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "</span></div></td><td class=\"px-6 py-4 whitespace-nowrap\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
@ -730,7 +747,7 @@ func TabloListRow(tablo TabloCardView) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "</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\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "</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\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
@ -738,42 +755,46 @@ func TabloListRow(tablo TabloCardView) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var36 string
|
||||
templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.JoinStringErrs(tablo.CreatedAtLabel)
|
||||
var templ_7745c5c3_Var33 string
|
||||
templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinStringErrs(tablo.CreatedAtLabel)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 208, Col: 26}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 232, Col: 26}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var36))
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var33))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "</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=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "</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=\"project-progress-bar h-2 rounded-full transition-all\" style=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var37 string
|
||||
templ_7745c5c3_Var37, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(progressInlineStyle(tablo.Progress))
|
||||
var templ_7745c5c3_Var34 string
|
||||
templ_7745c5c3_Var34, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(progressInlineStyle(tablo.Progress))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 214, Col: 106}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 238, Col: 114}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var37))
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var34))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "\"></div></div><span class=\"text-sm font-semibold text-gray-900 dark:text-gray-100 w-8 text-right\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "\"></div></div><span class=\"text-sm font-semibold text-gray-900 dark:text-gray-100 w-8 text-right\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var38 string
|
||||
templ_7745c5c3_Var38, templ_7745c5c3_Err = templ.JoinStringErrs(tablo.ProgressLabel)
|
||||
var templ_7745c5c3_Var35 string
|
||||
templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.JoinStringErrs(tablo.ProgressLabel)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 216, Col: 109}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 240, Col: 109}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var38))
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var35))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "</span></div></td><td class=\"px-6 py-4 text-right\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "</span></div></td><td class=\"px-6 py-4 text-right\"><div class=\"flex items-center justify-end gap-1\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = EditTabloButton(tablo.EditRequestURL).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
@ -781,7 +802,7 @@ func TabloListRow(tablo TabloCardView) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "</td></tr>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "</div></td></tr>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
@ -805,9 +826,9 @@ func CreateTabloModal(vm TablosPageViewModel) templ.Component {
|
|||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var39 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var39 == nil {
|
||||
templ_7745c5c3_Var39 = templ.NopComponent
|
||||
templ_7745c5c3_Var36 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var36 == nil {
|
||||
templ_7745c5c3_Var36 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = ui.Modal(ui.ModalProps{
|
||||
|
|
@ -837,12 +858,12 @@ func TabloListHead() templ.Component {
|
|||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var40 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var40 == nil {
|
||||
templ_7745c5c3_Var40 = templ.NopComponent
|
||||
templ_7745c5c3_Var37 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var37 == nil {
|
||||
templ_7745c5c3_Var37 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "<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_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "<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>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
@ -866,9 +887,9 @@ func TabloListBody(tablos []TabloCardView) templ.Component {
|
|||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var41 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var41 == nil {
|
||||
templ_7745c5c3_Var41 = templ.NopComponent
|
||||
templ_7745c5c3_Var38 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var38 == nil {
|
||||
templ_7745c5c3_Var38 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
for _, tablo := range tablos {
|
||||
|
|
@ -897,69 +918,69 @@ func CreateTabloModalBody(vm TablosPageViewModel) templ.Component {
|
|||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var42 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var42 == nil {
|
||||
templ_7745c5c3_Var42 = templ.NopComponent
|
||||
templ_7745c5c3_Var39 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var39 == nil {
|
||||
templ_7745c5c3_Var39 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "<form hx-post=\"/tablos\" hx-target=\"#app-main-content\" hx-swap=\"outerHTML\" class=\"flex flex-col gap-4\"><input type=\"hidden\" name=\"view\" value=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "<form hx-post=\"/tablos\" hx-target=\"#app-main-content\" hx-swap=\"outerHTML\" class=\"flex flex-col gap-4\"><input type=\"hidden\" name=\"view\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var43 string
|
||||
templ_7745c5c3_Var43, templ_7745c5c3_Err = templ.JoinStringErrs(vm.View)
|
||||
var templ_7745c5c3_Var40 string
|
||||
templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.JoinStringErrs(vm.View)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 255, Col: 50}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 282, Col: 50}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var43))
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var40))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "\"> <input type=\"hidden\" name=\"status\" value=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "\"> <input type=\"hidden\" name=\"status\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var44 string
|
||||
templ_7745c5c3_Var44, templ_7745c5c3_Err = templ.JoinStringErrs(vm.Status)
|
||||
var templ_7745c5c3_Var41 string
|
||||
templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.JoinStringErrs(vm.Status)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 256, Col: 54}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 283, Col: 54}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var44))
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var41))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "\"> <input type=\"hidden\" name=\"q\" value=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "\"> <input type=\"hidden\" name=\"q\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var45 string
|
||||
templ_7745c5c3_Var45, templ_7745c5c3_Err = templ.JoinStringErrs(vm.Query)
|
||||
var templ_7745c5c3_Var42 string
|
||||
templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinStringErrs(vm.Query)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 257, Col: 48}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 284, Col: 48}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var45))
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "\"> <input type=\"hidden\" name=\"modal\" value=\"create\"> ")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "\"> <input type=\"hidden\" name=\"modal\" value=\"create\"> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if vm.ErrorMessage != "" {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "<div class=\"mb-1 rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "<div class=\"mb-1 rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var46 string
|
||||
templ_7745c5c3_Var46, templ_7745c5c3_Err = templ.JoinStringErrs(vm.ErrorMessage)
|
||||
var templ_7745c5c3_Var43 string
|
||||
templ_7745c5c3_Var43, templ_7745c5c3_Err = templ.JoinStringErrs(vm.ErrorMessage)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 260, Col: 112}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 287, Col: 112}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var46))
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var43))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, "</div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
@ -974,38 +995,55 @@ func CreateTabloModalBody(vm TablosPageViewModel) templ.Component {
|
|||
Placeholder: "Nom du projet",
|
||||
Type: "text",
|
||||
}),
|
||||
Error: vm.ErrorMessage,
|
||||
}).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, "<div class=\"flex items-center justify-end gap-3\"><a href=\"")
|
||||
templ_7745c5c3_Err = ui.FormField(ui.FormFieldProps{
|
||||
Label: "Couleur",
|
||||
For: "tablo-color",
|
||||
Field: ui.Input(ui.InputProps{
|
||||
ID: "tablo-color",
|
||||
Name: "color",
|
||||
Value: vm.FormColor,
|
||||
Placeholder: "#3B82F6",
|
||||
Type: "text",
|
||||
Attrs: templ.Attributes{
|
||||
"pattern": "^#[0-9A-Fa-f]{6}$",
|
||||
"autocomplete": "off",
|
||||
},
|
||||
}),
|
||||
}).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var47 templ.SafeURL
|
||||
templ_7745c5c3_Var47, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(vm.CloseModalHref()))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 276, Col: 45}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var47))
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "<div class=\"flex items-center justify-end gap-3\"><a href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, "\" hx-get=\"")
|
||||
var templ_7745c5c3_Var44 templ.SafeURL
|
||||
templ_7745c5c3_Var44, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(vm.CloseModalHref()))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 317, Col: 45}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var44))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var48 string
|
||||
templ_7745c5c3_Var48, templ_7745c5c3_Err = templ.JoinStringErrs(vm.CloseModalHref())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 277, Col: 32}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var48))
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "\" hx-get=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, "\" 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>")
|
||||
var templ_7745c5c3_Var45 string
|
||||
templ_7745c5c3_Var45, templ_7745c5c3_Err = templ.JoinStringErrs(vm.CloseModalHref())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 318, Col: 32}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var45))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, "\" 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>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
@ -1018,7 +1056,266 @@ func CreateTabloModalBody(vm TablosPageViewModel) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, "</div></form>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, "</div></form>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func EditTabloModal(vm TablosPageViewModel) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var46 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var46 == nil {
|
||||
templ_7745c5c3_Var46 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = ui.Modal(ui.ModalProps{
|
||||
Title: "Modifier le projet",
|
||||
Body: EditTabloModalBody(vm),
|
||||
}).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func EditTabloColorField(vm TablosPageViewModel) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var47 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var47 == nil {
|
||||
templ_7745c5c3_Var47 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, "<div class=\"flex items-center gap-3\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = ui.Input(ui.InputProps{
|
||||
ID: "edit-tablo-color",
|
||||
Name: "color",
|
||||
Value: vm.FormColor,
|
||||
Placeholder: "#3B82F6",
|
||||
Type: "text",
|
||||
Attrs: templ.Attributes{
|
||||
"pattern": "^#[0-9A-Fa-f]{6}$",
|
||||
"autocomplete": "off",
|
||||
"oninput": "document.getElementById('edit-tablo-color-picker').value=this.value",
|
||||
},
|
||||
}).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, "<input id=\"edit-tablo-color-picker\" type=\"color\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var48 string
|
||||
templ_7745c5c3_Var48, templ_7745c5c3_Err = templ.JoinStringErrs(vm.FormColor)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 360, Col: 23}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var48))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, "\" class=\"ui-input tablo-color-picker\" oninput=\"document.getElementById('edit-tablo-color').value=this.value\"></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func EditTabloModalBody(vm TablosPageViewModel) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var49 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var49 == nil {
|
||||
templ_7745c5c3_Var49 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 65, "<form hx-post=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var50 string
|
||||
templ_7745c5c3_Var50, templ_7745c5c3_Err = templ.JoinStringErrs(vm.EditSubmitHref())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 369, Col: 31}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var50))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 66, "\" hx-target=\"#app-main-content\" hx-swap=\"outerHTML\" class=\"flex flex-col gap-4\"><input type=\"hidden\" name=\"view\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var51 string
|
||||
templ_7745c5c3_Var51, templ_7745c5c3_Err = templ.JoinStringErrs(vm.View)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 374, Col: 50}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var51))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 67, "\"> <input type=\"hidden\" name=\"status\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var52 string
|
||||
templ_7745c5c3_Var52, templ_7745c5c3_Err = templ.JoinStringErrs(vm.Status)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 375, Col: 54}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var52))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 68, "\"> <input type=\"hidden\" name=\"q\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var53 string
|
||||
templ_7745c5c3_Var53, templ_7745c5c3_Err = templ.JoinStringErrs(vm.Query)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 376, Col: 48}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var53))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 69, "\"> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if vm.ErrorMessage != "" {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 70, "<div class=\"mb-1 rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var54 string
|
||||
templ_7745c5c3_Var54, templ_7745c5c3_Err = templ.JoinStringErrs(vm.ErrorMessage)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 378, Col: 112}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var54))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, "</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = ui.FormField(ui.FormFieldProps{
|
||||
Label: "Nom du projet",
|
||||
For: "edit-tablo-name",
|
||||
Field: ui.Input(ui.InputProps{
|
||||
ID: "edit-tablo-name",
|
||||
Name: "name",
|
||||
Value: vm.FormName,
|
||||
Placeholder: "Nom du projet",
|
||||
Type: "text",
|
||||
}),
|
||||
}).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = ui.FormField(ui.FormFieldProps{
|
||||
Label: "Couleur",
|
||||
For: "edit-tablo-color",
|
||||
Field: EditTabloColorField(vm),
|
||||
Hint: "Utilisez le champ hex ou le sélecteur pour choisir une couleur au format #RRGGBB.",
|
||||
}).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 72, "<div class=\"flex items-center justify-end gap-3\"><a href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var55 templ.SafeURL
|
||||
templ_7745c5c3_Var55, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(vm.CloseModalHref()))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 399, Col: 45}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var55))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 73, "\" hx-get=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var56 string
|
||||
templ_7745c5c3_Var56, templ_7745c5c3_Err = templ.JoinStringErrs(vm.CloseModalHref())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 400, Col: 32}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var56))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 74, "\" 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>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = ui.Button(ui.ButtonProps{
|
||||
Label: "Enregistrer",
|
||||
Variant: ui.ButtonVariantDefault,
|
||||
Size: ui.SizeMD,
|
||||
Type: "submit",
|
||||
}).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 75, "</div></form>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,50 +5,57 @@ import (
|
|||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/a-h/templ"
|
||||
"xtablo-backend/internal/web/ui"
|
||||
)
|
||||
|
||||
type TabloCardView struct {
|
||||
ID string
|
||||
Name string
|
||||
Status string
|
||||
StatusLabel string
|
||||
StatusClass string
|
||||
StatusTone string
|
||||
Progress int
|
||||
CreatedAtLabel string
|
||||
CardDateLabel string
|
||||
ProgressLabel string
|
||||
DeleteURL string
|
||||
ID string
|
||||
Name string
|
||||
Color string
|
||||
Status string
|
||||
StatusLabel string
|
||||
StatusClass string
|
||||
StatusTone string
|
||||
Progress int
|
||||
CreatedAtLabel string
|
||||
CardDateLabel string
|
||||
ProgressLabel string
|
||||
DeleteURL string
|
||||
DeleteRequestURL string
|
||||
IconKind string
|
||||
IconBgClass string
|
||||
IconFgClass string
|
||||
Accent string
|
||||
Initial string
|
||||
EditRequestURL string
|
||||
IconKind string
|
||||
IconBgClass string
|
||||
IconFgClass string
|
||||
Accent string
|
||||
Initial string
|
||||
}
|
||||
|
||||
type TablosPageViewModel struct {
|
||||
DisplayName string
|
||||
View string
|
||||
Query string
|
||||
Status string
|
||||
ModalOpen bool
|
||||
FormName string
|
||||
ErrorMessage string
|
||||
Tablos []TabloCardView
|
||||
DisplayName string
|
||||
View string
|
||||
Query string
|
||||
Status string
|
||||
ModalKind string
|
||||
EditingTabloID string
|
||||
FormName string
|
||||
FormColor string
|
||||
ErrorMessage string
|
||||
Tablos []TabloCardView
|
||||
}
|
||||
|
||||
func NewTablosPageViewModel(displayName string, view string, query string, status string, modalOpen bool, formName string, errorMessage string, tablos []TabloCardView) TablosPageViewModel {
|
||||
func NewTablosPageViewModel(displayName string, view string, query string, status string, modalKind string, editingTabloID string, formName string, formColor string, errorMessage string, tablos []TabloCardView) TablosPageViewModel {
|
||||
return TablosPageViewModel{
|
||||
DisplayName: displayName,
|
||||
View: normalizedView(view),
|
||||
Query: strings.TrimSpace(query),
|
||||
Status: normalizedStatus(status),
|
||||
ModalOpen: modalOpen,
|
||||
FormName: strings.TrimSpace(formName),
|
||||
ErrorMessage: strings.TrimSpace(errorMessage),
|
||||
Tablos: tablos,
|
||||
DisplayName: displayName,
|
||||
View: normalizedView(view),
|
||||
Query: strings.TrimSpace(query),
|
||||
Status: normalizedStatus(status),
|
||||
ModalKind: normalizedModalKind(modalKind),
|
||||
EditingTabloID: strings.TrimSpace(editingTabloID),
|
||||
FormName: strings.TrimSpace(formName),
|
||||
FormColor: normalizedFormColor(modalKind, formColor),
|
||||
ErrorMessage: strings.TrimSpace(errorMessage),
|
||||
Tablos: tablos,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -60,6 +67,18 @@ func (vm TablosPageViewModel) HasTablos() bool {
|
|||
return len(vm.Tablos) > 0
|
||||
}
|
||||
|
||||
func (vm TablosPageViewModel) HasModal() bool {
|
||||
return vm.ModalKind != ""
|
||||
}
|
||||
|
||||
func (vm TablosPageViewModel) IsCreateModal() bool {
|
||||
return vm.ModalKind == "create"
|
||||
}
|
||||
|
||||
func (vm TablosPageViewModel) IsEditModal() bool {
|
||||
return vm.ModalKind == "edit"
|
||||
}
|
||||
|
||||
func (vm TablosPageViewModel) StatusHref(status string) string {
|
||||
values := vm.baseValues()
|
||||
values.Set("status", normalizedStatus(status))
|
||||
|
|
@ -94,11 +113,20 @@ func (vm TablosPageViewModel) CreateModalHref() string {
|
|||
return "/tablos?" + values.Encode()
|
||||
}
|
||||
|
||||
func (vm TablosPageViewModel) EditModalHref(tabloID string) string {
|
||||
values := vm.baseValues()
|
||||
return "/tablos/" + strings.TrimSpace(tabloID) + "/edit?" + values.Encode()
|
||||
}
|
||||
|
||||
func (vm TablosPageViewModel) CloseModalHref() string {
|
||||
values := vm.baseValues()
|
||||
return "/tablos?" + values.Encode()
|
||||
}
|
||||
|
||||
func (vm TablosPageViewModel) EditSubmitHref() string {
|
||||
return "/tablos/" + vm.EditingTabloID
|
||||
}
|
||||
|
||||
func (vm TablosPageViewModel) HasSearch() bool {
|
||||
return vm.Query != ""
|
||||
}
|
||||
|
|
@ -119,6 +147,26 @@ func normalizedStatus(status string) string {
|
|||
}
|
||||
}
|
||||
|
||||
func normalizedModalKind(kind string) string {
|
||||
switch kind {
|
||||
case "create", "edit":
|
||||
return kind
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func normalizedFormColor(modalKind string, color string) string {
|
||||
trimmed := strings.TrimSpace(color)
|
||||
if trimmed != "" {
|
||||
return trimmed
|
||||
}
|
||||
if normalizedModalKind(modalKind) == "create" {
|
||||
return "#3B82F6"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (vm TablosPageViewModel) baseValues() url.Values {
|
||||
values := url.Values{}
|
||||
values.Set("view", vm.View)
|
||||
|
|
@ -159,3 +207,11 @@ func badgeVariantForTone(tone string) ui.BadgeVariant {
|
|||
return ui.BadgeVariantInfo
|
||||
}
|
||||
}
|
||||
|
||||
func projectColorVariableStyle(color string) templ.SafeCSS {
|
||||
return templ.SanitizeCSS("--project-color", templ.SafeCSSProperty(strings.TrimSpace(color)))
|
||||
}
|
||||
|
||||
func backgroundColorStyle(color string) templ.SafeCSS {
|
||||
return templ.SanitizeCSS("background-color", templ.SafeCSSProperty(strings.TrimSpace(color)))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@ func newRouterWithHandler(authHandler *handlers.AuthHandler) http.Handler {
|
|||
mux.Get("/files", authHandler.GetFilesPage())
|
||||
mux.Get("/feedback", authHandler.GetFeedbackPage())
|
||||
mux.Post("/tablos", authHandler.PostTablos())
|
||||
mux.Get("/tablos/{tabloID}/edit", authHandler.GetEditTabloModal())
|
||||
mux.Post("/tablos/{tabloID}", authHandler.PostTabloUpdate())
|
||||
mux.Delete("/tablos/{tabloID}", authHandler.DeleteTablo())
|
||||
mux.Get("/login", authHandler.GetLoginPage())
|
||||
mux.Get("/signup", authHandler.GetSignupPage())
|
||||
|
|
|
|||
|
|
@ -983,7 +983,7 @@ input {
|
|||
}
|
||||
|
||||
.project-card-top {
|
||||
align-items: flex-start;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 1rem;
|
||||
|
|
@ -1468,6 +1468,19 @@ input {
|
|||
display: inline-flex;
|
||||
}
|
||||
|
||||
.catalog-spacing-row {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.catalog-spacing-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.catalog-example-snippet {
|
||||
background: #111827;
|
||||
border-radius: 0.875rem;
|
||||
|
|
@ -1511,7 +1524,6 @@ input {
|
|||
background: transparent;
|
||||
border: 0;
|
||||
border-radius: 0.5rem;
|
||||
color: #6b7280;
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
|
|
@ -1523,11 +1535,64 @@ input {
|
|||
color 0.2s ease;
|
||||
}
|
||||
|
||||
.ui-icon-button:hover {
|
||||
.ui-icon-button-solid.ui-icon-button-neutral {
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.ui-icon-button-solid.ui-icon-button-neutral:hover {
|
||||
background: #f9fafb;
|
||||
color: #111827;
|
||||
}
|
||||
|
||||
.ui-space-x {
|
||||
display: inline-block;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.ui-space-y {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.ui-space-x-xs {
|
||||
width: 0.25rem;
|
||||
}
|
||||
|
||||
.ui-space-x-sm {
|
||||
width: 0.5rem;
|
||||
}
|
||||
|
||||
.ui-space-x-md {
|
||||
width: 0.75rem;
|
||||
}
|
||||
|
||||
.ui-space-x-lg {
|
||||
width: 1rem;
|
||||
}
|
||||
|
||||
.ui-space-x-xl {
|
||||
width: 1.5rem;
|
||||
}
|
||||
|
||||
.ui-space-y-xs {
|
||||
height: 0.25rem;
|
||||
}
|
||||
|
||||
.ui-space-y-sm {
|
||||
height: 0.5rem;
|
||||
}
|
||||
|
||||
.ui-space-y-md {
|
||||
height: 0.75rem;
|
||||
}
|
||||
|
||||
.ui-space-y-lg {
|
||||
height: 1rem;
|
||||
}
|
||||
|
||||
.ui-space-y-xl {
|
||||
height: 1.5rem;
|
||||
}
|
||||
|
||||
.ui-modal-backdrop {
|
||||
align-items: center;
|
||||
background: rgba(17, 24, 39, 0.52);
|
||||
|
|
@ -1587,16 +1652,24 @@ input {
|
|||
border: 0;
|
||||
box-shadow: none;
|
||||
appearance: none;
|
||||
color: #9ca3af;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.ui-icon-button-ghost.ui-icon-button-neutral,
|
||||
.ui-icon-button-ghost.ui-icon-button-danger {
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.project-card-top .borderless-icon-button {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.project-card-top .borderless-icon-button:hover {
|
||||
.project-card-top .borderless-icon-button.ui-icon-button-ghost.ui-icon-button-neutral:hover {
|
||||
color: #111827;
|
||||
}
|
||||
|
||||
.project-card-top .borderless-icon-button.ui-icon-button-ghost.ui-icon-button-danger:hover {
|
||||
color: #ef4444;
|
||||
}
|
||||
|
||||
|
|
@ -1621,7 +1694,11 @@ td.text-right .borderless-icon-button {
|
|||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
td.text-right .borderless-icon-button:hover {
|
||||
td.text-right .borderless-icon-button.ui-icon-button-ghost.ui-icon-button-neutral:hover {
|
||||
color: #111827;
|
||||
}
|
||||
|
||||
td.text-right .borderless-icon-button.ui-icon-button-ghost.ui-icon-button-danger:hover {
|
||||
color: #ef4444;
|
||||
}
|
||||
|
||||
|
|
@ -1634,6 +1711,7 @@ td.text-right .borderless-icon-button:hover {
|
|||
|
||||
.project-avatar {
|
||||
align-items: center;
|
||||
background: var(--project-color, #3b82f6);
|
||||
border-radius: 0.85rem;
|
||||
color: #fff;
|
||||
display: inline-flex;
|
||||
|
|
@ -1645,6 +1723,11 @@ td.text-right .borderless-icon-button:hover {
|
|||
width: 3rem;
|
||||
}
|
||||
|
||||
.project-list-icon {
|
||||
background: var(--project-color, #3b82f6);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.project-accent-blue {
|
||||
background: #3b82f6;
|
||||
}
|
||||
|
|
@ -1697,10 +1780,17 @@ td.text-right .borderless-icon-button:hover {
|
|||
}
|
||||
|
||||
.project-progress-bar {
|
||||
background: var(--project-color, #3b82f6);
|
||||
border-radius: 999px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.tablo-color-picker {
|
||||
max-width: 5rem;
|
||||
min-height: 44px;
|
||||
padding: 0.4rem;
|
||||
}
|
||||
|
||||
.overview-more-row {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
--color-green-50: oklch(98.2% 0.018 155.826);
|
||||
--color-green-200: oklch(92.5% 0.084 155.995);
|
||||
--color-green-400: oklch(79.2% 0.209 151.711);
|
||||
--color-green-500: oklch(72.3% 0.219 149.579);
|
||||
--color-green-600: oklch(62.7% 0.194 149.214);
|
||||
--color-green-800: oklch(44.8% 0.119 151.328);
|
||||
--color-green-950: oklch(26.6% 0.065 152.934);
|
||||
|
|
@ -60,6 +59,9 @@
|
|||
.absolute {
|
||||
position: absolute;
|
||||
}
|
||||
.fixed {
|
||||
position: fixed;
|
||||
}
|
||||
.relative {
|
||||
position: relative;
|
||||
}
|
||||
|
|
@ -199,6 +201,9 @@
|
|||
.justify-end {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.gap-1 {
|
||||
gap: calc(var(--spacing) * 1);
|
||||
}
|
||||
.gap-1\.5 {
|
||||
gap: calc(var(--spacing) * 1.5);
|
||||
}
|
||||
|
|
@ -298,9 +303,6 @@
|
|||
.bg-green-50 {
|
||||
background-color: var(--color-green-50);
|
||||
}
|
||||
.bg-green-500 {
|
||||
background-color: var(--color-green-500);
|
||||
}
|
||||
.bg-purple-50 {
|
||||
background-color: var(--color-purple-50);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue