feat(19): list view as proper table — Projet/Statut/Créé le/Progression/Actions
- Two separate containers: #tablos-grid (card grid) and #tablos-table (table), toggled via .hidden - TabloListRow: new templ for <tr> with icon+title, Actif/Archivé badge, date, progress bar, trash - JS: setTablosView toggles hidden on both containers; filterTablos targets both article and tr elements - Remove old .tablo-list-row / #tablos-list[data-view] CSS (no longer needed) Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
90d43f98bc
commit
a575ab93c6
3 changed files with 96 additions and 77 deletions
|
|
@ -844,57 +844,7 @@
|
|||
height: 100%;
|
||||
}
|
||||
|
||||
/* Transparent grid-item wrapper — children participate in grid/flex directly */
|
||||
.tablo-card-wrapper {
|
||||
display: contents;
|
||||
}
|
||||
|
||||
/* List-view row (hidden by default) */
|
||||
.tablo-list-row {
|
||||
display: none;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
padding: 0.75rem 1rem;
|
||||
border: 1px solid var(--color-border-subtle);
|
||||
border-radius: 0.75rem;
|
||||
background: var(--color-surface-default);
|
||||
}
|
||||
|
||||
/* Title cell in list row */
|
||||
.tablo-list-row-title {
|
||||
flex: 1;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* Metadata cells in list row */
|
||||
.tablo-list-row-meta {
|
||||
color: var(--color-text-muted);
|
||||
display: flex;
|
||||
font-size: 0.875rem;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
/* data-view="list" toggle rules */
|
||||
#tablos-list[data-view="list"] {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
#tablos-list[data-view="list"] .project-card {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#tablos-list[data-view="list"] .tablo-list-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* Single-column layout in list view */
|
||||
#tablos-list[data-view="list"] {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
/* Grid/table view is toggled via .hidden Tailwind class on #tablos-grid / #tablos-table */
|
||||
|
||||
/* ── View toggle tabs ─────────────────────────────────────── */
|
||||
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -49,8 +49,8 @@ templ TablosDashboard(user *auth.User, csrfToken string, activePath string, tabl
|
|||
<button type="button" class="filter-tab" data-filter-btn="active" onclick="filterTablos('active')">Actif</button>
|
||||
<button type="button" class="filter-tab" data-filter-btn="archived" onclick="filterTablos('archived')">Archivé</button>
|
||||
</div>
|
||||
<!-- Card/list grid -->
|
||||
<div id="tablos-list" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 sm:gap-6" data-view="grid">
|
||||
<!-- Grid view -->
|
||||
<div id="tablos-grid" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 sm:gap-6">
|
||||
if len(cards) == 0 {
|
||||
@TablosEmptyState()
|
||||
} else {
|
||||
|
|
@ -59,28 +59,52 @@ templ TablosDashboard(user *auth.User, csrfToken string, activePath string, tabl
|
|||
}
|
||||
}
|
||||
</div>
|
||||
<!-- List view (hidden by default) -->
|
||||
<div id="tablos-table" class="hidden bg-white rounded-xl border border-[#EAECF0] overflow-x-auto">
|
||||
<table class="w-full min-w-[600px]">
|
||||
<thead class="bg-gray-50 border-b border-[#EAECF0]">
|
||||
<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>
|
||||
<th class="px-6 py-3 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">Créé le</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">Progression</th>
|
||||
<th class="px-6 py-3 w-12"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
for _, card := range cards {
|
||||
@TabloListRow(card, csrfToken)
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
function setTablosView(v) {
|
||||
document.getElementById('tablos-list').dataset.view = v;
|
||||
document.getElementById('tablos-grid').classList.toggle('hidden', v === 'list');
|
||||
document.getElementById('tablos-table').classList.toggle('hidden', v === 'grid');
|
||||
document.querySelectorAll('[data-view-btn]').forEach(function(b) {
|
||||
b.classList.toggle('is-active', b.dataset.viewBtn === v);
|
||||
});
|
||||
}
|
||||
function filterTablos(s) {
|
||||
document.querySelectorAll('#tablos-list article').forEach(function(el) {
|
||||
document.querySelectorAll('#tablos-grid article').forEach(function(el) {
|
||||
el.style.display = (s === 'tous' || el.dataset.displayStatus === s) ? '' : 'none';
|
||||
});
|
||||
document.querySelectorAll('#tablos-table tbody tr').forEach(function(el) {
|
||||
el.style.display = (s === 'tous' || el.dataset.displayStatus === s) ? '' : 'none';
|
||||
});
|
||||
document.querySelectorAll('[data-filter-btn]').forEach(function(b) {
|
||||
b.classList.toggle('is-active', b.dataset.filterBtn === s);
|
||||
});
|
||||
}
|
||||
// Card click → navigate to tablo detail (stop on delete zone)
|
||||
document.getElementById('tablos-list').addEventListener('click', function(e) {
|
||||
var article = e.target.closest('article[data-href]');
|
||||
if (article && !e.target.closest('.tablo-delete-zone')) {
|
||||
window.location = article.dataset.href;
|
||||
}
|
||||
document.getElementById('tablos-grid').addEventListener('click', function(e) {
|
||||
var a = e.target.closest('article[data-href]');
|
||||
if (a && !e.target.closest('.tablo-delete-zone')) window.location = a.dataset.href;
|
||||
});
|
||||
document.getElementById('tablos-table').addEventListener('click', function(e) {
|
||||
var r = e.target.closest('tr[data-href]');
|
||||
if (r && !e.target.closest('.tablo-delete-zone')) window.location = r.dataset.href;
|
||||
});
|
||||
</script>
|
||||
}
|
||||
|
|
@ -189,21 +213,66 @@ templ TabloProjectCard(card TabloCardView, csrfToken string) {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- List row (hidden by default, shown when data-view="list") -->
|
||||
<div class="tablo-list-row" onclick="event.stopPropagation()">
|
||||
<a class="flex items-center gap-4 w-full px-4 py-3 hover:bg-gray-50" href={ templ.SafeURL("/tablos/" + card.Tablo.ID.String()) }>
|
||||
if tabloDisplayStatus(card.Progress, card.TotalTasks) == "termine" {
|
||||
<span class="px-2 py-0.5 rounded-full text-xs font-medium bg-green-50 text-green-600 border border-green-200 shrink-0">Terminé</span>
|
||||
} else if tabloDisplayStatus(card.Progress, card.TotalTasks) == "en-cours" {
|
||||
<span class="px-2 py-0.5 rounded-full text-xs font-medium bg-[#FFF4E2] text-[#DB9729] border border-[#DB9729] shrink-0">En cours</span>
|
||||
</article>
|
||||
}
|
||||
|
||||
// TabloListRow renders one table row for the list view.
|
||||
templ TabloListRow(card TabloCardView, csrfToken string) {
|
||||
<tr
|
||||
class="border-t border-[#EAECF0] hover:bg-gray-50 transition-colors cursor-pointer"
|
||||
data-display-status={ card.Tablo.Status }
|
||||
data-href={ "/tablos/" + card.Tablo.ID.String() }
|
||||
>
|
||||
<!-- Projet: icon + title -->
|
||||
<td class="px-6 py-4">
|
||||
<div class="flex items-center gap-3">
|
||||
if card.Tablo.Color.Valid && card.Tablo.Color.String != "" {
|
||||
<div class="w-8 h-8 rounded-lg flex items-center justify-center shrink-0 overflow-hidden" style={ "background-color: " + card.Tablo.Color.String }>
|
||||
<span class="text-white font-bold text-sm">
|
||||
if len(card.Tablo.Title) > 0 {
|
||||
{ string([]rune(card.Tablo.Title)[0:1]) }
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
} else {
|
||||
<span class="px-2 py-0.5 rounded-full text-xs font-medium bg-blue-50 text-blue-600 border border-blue-200 shrink-0">À faire</span>
|
||||
<div class="w-8 h-8 rounded-lg flex items-center justify-center shrink-0 overflow-hidden bg-blue-500">
|
||||
<span class="text-white font-bold text-sm">
|
||||
if len(card.Tablo.Title) > 0 {
|
||||
{ string([]rune(card.Tablo.Title)[0:1]) }
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
<span class="tablo-list-row-title flex-1">{ card.Tablo.Title }</span>
|
||||
<span class="tablo-list-row-meta shrink-0">{ card.Tablo.CreatedAt.Time.Format("2 Jan 2006") }</span>
|
||||
<span class="tablo-list-row-meta shrink-0">{ strconv.Itoa(card.DoneTasks) }/{ strconv.Itoa(card.TotalTasks) }</span>
|
||||
</a>
|
||||
<div class="tablo-delete-zone shrink-0" onclick="event.stopPropagation()">
|
||||
<span class="font-medium text-gray-900 truncate">{ card.Tablo.Title }</span>
|
||||
</div>
|
||||
</td>
|
||||
<!-- Statut -->
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
if card.Tablo.Status == "archived" {
|
||||
<span class="px-3 py-1 rounded-full text-sm font-medium bg-gray-100 text-gray-600 border border-gray-200">Archivé</span>
|
||||
} else {
|
||||
<span class="px-3 py-1 rounded-full text-sm font-medium bg-green-50 text-green-600 border border-green-200">Actif</span>
|
||||
}
|
||||
</td>
|
||||
<!-- Créé le -->
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-600">
|
||||
<div class="flex items-center gap-1.5">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4 shrink-0" aria-hidden="true"><path d="M8 2v4"></path><path d="M16 2v4"></path><rect width="18" height="18" x="3" y="4" rx="2"></rect><path d="M3 10h18"></path></svg>
|
||||
{ card.Tablo.CreatedAt.Time.Format("2 Jan 2006") }
|
||||
</div>
|
||||
</td>
|
||||
<!-- Progression -->
|
||||
<td class="px-6 py-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="flex-1 bg-gray-200 rounded-full h-2 min-w-[80px]">
|
||||
<div class="bg-green-500 h-2 rounded-full" style={ "width: " + strconv.Itoa(card.Progress) + "%" }></div>
|
||||
</div>
|
||||
<span class="text-sm font-semibold text-gray-900 w-8 text-right">{ strconv.Itoa(card.Progress) }%</span>
|
||||
</div>
|
||||
</td>
|
||||
<!-- Actions -->
|
||||
<td class="px-6 py-4 text-right">
|
||||
<div class="tablo-delete-zone inline-flex">
|
||||
@ui.IconButton(ui.IconButtonProps{
|
||||
Label: "Delete tablo",
|
||||
Icon: "trash",
|
||||
|
|
@ -217,8 +286,8 @@ templ TabloProjectCard(card TabloCardView, csrfToken string) {
|
|||
},
|
||||
})
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
|
||||
// TabloCard renders a single tablo as a ui.Card on the dashboard.
|
||||
|
|
|
|||
Loading…
Reference in a new issue