mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 20:01:09 +02:00
feat(todo/web): add page minimize/restore tabs, inline task creation, i18n completed times
- Secondary pages can be minimized to compact tabs above the fokus track - Minimized tabs show centered with rounded corners, click to restore - Add inline task creation input at the bottom of every page (except completed) - Completed page shows date + time per task (i18n: "14:32 Uhr" DE, "14:32" EN) - To Do page shows recently completed tasks with time - Add + button next to minimized tabs to open page picker - PagePicker auto-scrolls into view when opened - i18n keys added for all 5 languages (DE/EN/FR/ES/IT) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e449172932
commit
6d51d3eefb
7 changed files with 401 additions and 33 deletions
|
|
@ -1,8 +1,9 @@
|
|||
<script lang="ts">
|
||||
import { getContext } from 'svelte';
|
||||
import { isToday, isPast, startOfDay, addDays } from 'date-fns';
|
||||
import { isToday, isPast, startOfDay, addDays, subHours, format } from 'date-fns';
|
||||
import { t } from 'svelte-i18n';
|
||||
import type { Task } from '@todo/shared';
|
||||
import { X } from '@manacore/shared-icons';
|
||||
import { X, Circle, Minus } from '@manacore/shared-icons';
|
||||
import KanbanTaskCard from '../kanban/KanbanTaskCard.svelte';
|
||||
import { tasksStore } from '$lib/stores/tasks.svelte';
|
||||
import { todoSettings } from '$lib/stores/settings.svelte';
|
||||
|
|
@ -10,9 +11,10 @@
|
|||
interface Props {
|
||||
pageId: string;
|
||||
onClose: () => void;
|
||||
onMinimize?: () => void;
|
||||
}
|
||||
|
||||
let { pageId, onClose }: Props = $props();
|
||||
let { pageId, onClose, onMinimize }: Props = $props();
|
||||
|
||||
const tasksCtx: { readonly value: Task[] } = getContext('tasks');
|
||||
|
||||
|
|
@ -35,8 +37,14 @@
|
|||
const weekEnd = addDays(today, 7);
|
||||
|
||||
switch (pageId) {
|
||||
case 'todo':
|
||||
return tasks.filter((t) => !t.isCompleted);
|
||||
case 'todo': {
|
||||
const recentCutoff = subHours(new Date(), 24);
|
||||
return tasks.filter(
|
||||
(t) =>
|
||||
!t.isCompleted ||
|
||||
(t.isCompleted && t.completedAt && new Date(t.completedAt) >= recentCutoff)
|
||||
);
|
||||
}
|
||||
case 'completed':
|
||||
return tasks.filter((t) => t.isCompleted);
|
||||
case 'today':
|
||||
|
|
@ -106,6 +114,40 @@
|
|||
};
|
||||
|
||||
let sheetWidth = $derived(PAGE_WIDTH_MAP[todoSettings.pageWidth] || PAGE_WIDTH_MAP.medium);
|
||||
|
||||
let openTasks = $derived(
|
||||
pageId === 'todo' ? filteredTasks.filter((t) => !t.isCompleted) : filteredTasks
|
||||
);
|
||||
let recentlyCompleted = $derived(
|
||||
pageId === 'todo' ? filteredTasks.filter((t) => t.isCompleted) : []
|
||||
);
|
||||
|
||||
function formatCompletedTime(completedAt: string): string {
|
||||
const date = new Date(completedAt);
|
||||
const time = format(date, 'HH:mm');
|
||||
if (pageId === 'completed') {
|
||||
const dateStr = format(date, 'dd.MM.');
|
||||
return $t('secondaryPage.completedAtDateTime', { values: { date: dateStr, time } });
|
||||
}
|
||||
return $t('secondaryPage.completedAtTime', { values: { time } });
|
||||
}
|
||||
|
||||
let newTaskTitle = $state('');
|
||||
let inputEl = $state<HTMLInputElement | null>(null);
|
||||
|
||||
async function handleInlineCreate() {
|
||||
const title = newTaskTitle.trim();
|
||||
if (!title) return;
|
||||
const data: { title: string; dueDate?: string } = { title };
|
||||
if (pageId === 'today') {
|
||||
data.dueDate = new Date().toISOString();
|
||||
} else if (pageId === 'this-week') {
|
||||
data.dueDate = new Date().toISOString();
|
||||
}
|
||||
await tasksStore.createTask(data);
|
||||
newTaskTitle = '';
|
||||
inputEl?.focus();
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="secondary-page" style="width: {sheetWidth}">
|
||||
|
|
@ -115,18 +157,35 @@
|
|||
<span class="page-title">{meta.title}</span>
|
||||
<span class="task-count">{filteredTasks.length}</span>
|
||||
</div>
|
||||
<button class="close-btn" onclick={onClose} title="Seite schließen">
|
||||
<X size={14} />
|
||||
</button>
|
||||
<div class="header-actions">
|
||||
{#if onMinimize}
|
||||
<button class="header-btn" onclick={onMinimize} title="Minimieren">
|
||||
<Minus size={14} />
|
||||
</button>
|
||||
{/if}
|
||||
<button class="header-btn" onclick={onClose} title="Seite schließen">
|
||||
<X size={14} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="page-body">
|
||||
{#if filteredTasks.length === 0}
|
||||
<div class="empty-state">
|
||||
<p>Keine Aufgaben</p>
|
||||
</div>
|
||||
{:else}
|
||||
{#if pageId === 'completed'}
|
||||
{#each filteredTasks as task (task.id)}
|
||||
<div class="task-card-wrapper completed-task">
|
||||
<KanbanTaskCard
|
||||
{task}
|
||||
onToggleComplete={() => handleToggle(task)}
|
||||
onSave={(data) => handleUpdate(task.id, data)}
|
||||
onDelete={() => handleDelete(task.id)}
|
||||
/>
|
||||
{#if task.completedAt}
|
||||
<span class="completed-time">{formatCompletedTime(task.completedAt)}</span>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
{:else}
|
||||
{#each openTasks as task (task.id)}
|
||||
<div class="task-card-wrapper">
|
||||
<KanbanTaskCard
|
||||
{task}
|
||||
|
|
@ -136,6 +195,40 @@
|
|||
/>
|
||||
</div>
|
||||
{/each}
|
||||
|
||||
{#if recentlyCompleted.length > 0}
|
||||
<div class="completed-section">
|
||||
<span class="completed-label">{$t('secondaryPage.recentlyCompleted')}</span>
|
||||
{#each recentlyCompleted as task (task.id)}
|
||||
<div class="task-card-wrapper completed-task">
|
||||
<KanbanTaskCard
|
||||
{task}
|
||||
onToggleComplete={() => handleToggle(task)}
|
||||
onSave={(data) => handleUpdate(task.id, data)}
|
||||
onDelete={() => handleDelete(task.id)}
|
||||
/>
|
||||
{#if task.completedAt}
|
||||
<span class="completed-time">{formatCompletedTime(task.completedAt)}</span>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="inline-create">
|
||||
<span class="inline-create-icon">
|
||||
<Circle size={18} />
|
||||
</span>
|
||||
<input
|
||||
bind:this={inputEl}
|
||||
bind:value={newTaskTitle}
|
||||
class="inline-create-input"
|
||||
placeholder={$t('secondaryPage.newTaskPlaceholder')}
|
||||
onkeydown={(e) => {
|
||||
if (e.key === 'Enter') handleInlineCreate();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -213,7 +306,13 @@
|
|||
color: #6b7280;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
.header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.125rem;
|
||||
}
|
||||
|
||||
.header-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
|
@ -226,11 +325,11 @@
|
|||
cursor: pointer;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
.close-btn:hover {
|
||||
.header-btn:hover {
|
||||
background: rgba(0, 0, 0, 0.06);
|
||||
color: #374151;
|
||||
}
|
||||
:global(.dark) .close-btn:hover {
|
||||
:global(.dark) .header-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
color: #f3f4f6;
|
||||
}
|
||||
|
|
@ -248,14 +347,73 @@
|
|||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
.completed-section {
|
||||
margin-top: 1rem;
|
||||
padding-top: 0.75rem;
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
:global(.dark) .completed-section {
|
||||
border-top-color: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.completed-label {
|
||||
font-size: 0.6875rem;
|
||||
font-weight: 500;
|
||||
color: #9ca3af;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
margin-bottom: 0.5rem;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.completed-task {
|
||||
position: relative;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.completed-time {
|
||||
position: absolute;
|
||||
right: 0.5rem;
|
||||
top: 0.5rem;
|
||||
font-size: 0.6875rem;
|
||||
color: #9ca3af;
|
||||
font-variant-numeric: tabular-nums;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.inline-create {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 120px;
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem 0.25rem;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
.empty-state p {
|
||||
|
||||
.inline-create-icon {
|
||||
flex-shrink: 0;
|
||||
color: #d1d5db;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
:global(.dark) .inline-create-icon {
|
||||
color: #4b5563;
|
||||
}
|
||||
|
||||
.inline-create-input {
|
||||
flex: 1;
|
||||
border: none;
|
||||
background: transparent;
|
||||
font-size: 0.8125rem;
|
||||
color: #9ca3af;
|
||||
color: #374151;
|
||||
outline: none;
|
||||
}
|
||||
.inline-create-input::placeholder {
|
||||
color: #c0bfba;
|
||||
}
|
||||
:global(.dark) .inline-create-input {
|
||||
color: #f3f4f6;
|
||||
}
|
||||
:global(.dark) .inline-create-input::placeholder {
|
||||
color: #4b5563;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -224,5 +224,11 @@
|
|||
"toolbar": {
|
||||
"taskOptions": "Aufgaben-Optionen",
|
||||
"switchView": "Ansicht wechseln"
|
||||
},
|
||||
"secondaryPage": {
|
||||
"recentlyCompleted": "Kürzlich erledigt",
|
||||
"completedAtTime": "{time} Uhr",
|
||||
"completedAtDateTime": "{date}, {time} Uhr",
|
||||
"newTaskPlaceholder": "Neue Aufgabe…"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -224,5 +224,11 @@
|
|||
"toolbar": {
|
||||
"taskOptions": "Task options",
|
||||
"switchView": "Switch view"
|
||||
},
|
||||
"secondaryPage": {
|
||||
"recentlyCompleted": "Recently completed",
|
||||
"completedAtTime": "{time}",
|
||||
"completedAtDateTime": "{date}, {time}",
|
||||
"newTaskPlaceholder": "New task…"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -224,5 +224,11 @@
|
|||
"toolbar": {
|
||||
"taskOptions": "Opciones de tareas",
|
||||
"switchView": "Cambiar vista"
|
||||
},
|
||||
"secondaryPage": {
|
||||
"recentlyCompleted": "Completadas recientemente",
|
||||
"completedAtTime": "{time}",
|
||||
"completedAtDateTime": "{date}, {time}",
|
||||
"newTaskPlaceholder": "Nueva tarea…"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -224,5 +224,11 @@
|
|||
"toolbar": {
|
||||
"taskOptions": "Options des tâches",
|
||||
"switchView": "Changer de vue"
|
||||
},
|
||||
"secondaryPage": {
|
||||
"recentlyCompleted": "Récemment terminées",
|
||||
"completedAtTime": "{time} h",
|
||||
"completedAtDateTime": "{date}, {time} h",
|
||||
"newTaskPlaceholder": "Nouvelle tâche…"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -224,5 +224,11 @@
|
|||
"toolbar": {
|
||||
"taskOptions": "Opzioni attività",
|
||||
"switchView": "Cambia vista"
|
||||
},
|
||||
"secondaryPage": {
|
||||
"recentlyCompleted": "Completate di recente",
|
||||
"completedAtTime": "ore {time}",
|
||||
"completedAtDateTime": "{date}, ore {time}",
|
||||
"newTaskPlaceholder": "Nuova attività…"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
import { BoardViewRenderer } from '$lib/components/board-views';
|
||||
import { todoSettings, type PageWidth } from '$lib/stores/settings.svelte';
|
||||
import { boardViewsStore } from '$lib/stores/board-views.svelte';
|
||||
import { Plus } from '@manacore/shared-icons';
|
||||
import { Plus, Minus, X } from '@manacore/shared-icons';
|
||||
import PagePicker from '$lib/components/pages/PagePicker.svelte';
|
||||
import SecondaryPage from '$lib/components/pages/SecondaryPage.svelte';
|
||||
|
||||
|
|
@ -19,17 +19,31 @@
|
|||
|
||||
// ── Secondary Pages ─────────────────────────────────────
|
||||
let showPagePicker = $state(false);
|
||||
let openPages = $state<string[]>([]);
|
||||
let openPages = $state<{ id: string; minimized: boolean }[]>([]);
|
||||
|
||||
let expandedPages = $derived(openPages.filter((p) => !p.minimized));
|
||||
let minimizedPages = $derived(openPages.filter((p) => p.minimized));
|
||||
|
||||
function handleAddPage(pageId: string) {
|
||||
if (!openPages.includes(pageId)) {
|
||||
openPages = [...openPages, pageId];
|
||||
if (!openPages.some((p) => p.id === pageId)) {
|
||||
openPages = [...openPages, { id: pageId, minimized: false }];
|
||||
} else {
|
||||
// Restore if minimized
|
||||
openPages = openPages.map((p) => (p.id === pageId ? { ...p, minimized: false } : p));
|
||||
}
|
||||
showPagePicker = false;
|
||||
}
|
||||
|
||||
function handleRemovePage(pageId: string) {
|
||||
openPages = openPages.filter((p) => p !== pageId);
|
||||
openPages = openPages.filter((p) => p.id !== pageId);
|
||||
}
|
||||
|
||||
function handleMinimizePage(pageId: string) {
|
||||
openPages = openPages.map((p) => (p.id === pageId ? { ...p, minimized: true } : p));
|
||||
}
|
||||
|
||||
function handleRestorePage(pageId: string) {
|
||||
openPages = openPages.map((p) => (p.id === pageId ? { ...p, minimized: false } : p));
|
||||
}
|
||||
|
||||
function togglePagePicker() {
|
||||
|
|
@ -112,6 +126,25 @@
|
|||
activeView?.groupBy === 'status' || activeView?.groupBy === 'custom'
|
||||
);
|
||||
|
||||
const PAGE_META: Record<string, { title: string; color: string }> = {
|
||||
todo: { title: 'To Do', color: '#6B7280' },
|
||||
completed: { title: 'Erledigt', color: '#22C55E' },
|
||||
today: { title: 'Heute', color: '#F59E0B' },
|
||||
overdue: { title: 'Überfällig', color: '#EF4444' },
|
||||
all: { title: 'Alle Aufgaben', color: '#3B82F6' },
|
||||
'high-priority': { title: 'Hohe Priorität', color: '#EF4444' },
|
||||
'this-week': { title: 'Diese Woche', color: '#8B5CF6' },
|
||||
'no-date': { title: 'Ohne Datum', color: '#6B7280' },
|
||||
};
|
||||
|
||||
let pagePickerEl = $state<HTMLDivElement | null>(null);
|
||||
|
||||
$effect(() => {
|
||||
if (showPagePicker && pagePickerEl) {
|
||||
pagePickerEl.scrollIntoView({ behavior: 'smooth', inline: 'center', block: 'nearest' });
|
||||
}
|
||||
});
|
||||
|
||||
function handleKeydown(e: KeyboardEvent) {
|
||||
if (e.key === 'Escape' && editMode) {
|
||||
editModeCtx.set(false);
|
||||
|
|
@ -169,6 +202,37 @@
|
|||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Minimized Page Tabs -->
|
||||
{#if minimizedPages.length > 0}
|
||||
<div class="minimized-tabs">
|
||||
{#each minimizedPages as page (page.id)}
|
||||
{@const meta = PAGE_META[page.id] ?? { title: page.id, color: '#6B7280' }}
|
||||
<div
|
||||
class="minimized-tab"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
onclick={() => handleRestorePage(page.id)}
|
||||
>
|
||||
<span class="minimized-tab-dot" style="background-color: {meta.color}"></span>
|
||||
<span class="minimized-tab-title">{meta.title}</span>
|
||||
<button
|
||||
class="minimized-tab-close"
|
||||
onclick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleRemovePage(page.id);
|
||||
}}
|
||||
title="Schließen"
|
||||
>
|
||||
<X size={10} />
|
||||
</button>
|
||||
</div>
|
||||
{/each}
|
||||
<button class="minimized-tab-add" onclick={togglePagePicker} title="Neue Seite hinzufügen">
|
||||
<Plus size={14} />
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Board Content -->
|
||||
{#if activeView}
|
||||
<BoardViewRenderer
|
||||
|
|
@ -183,18 +247,24 @@
|
|||
>
|
||||
{#snippet trailing()}
|
||||
<!-- Secondary Pages -->
|
||||
{#each openPages as pageId (pageId)}
|
||||
<SecondaryPage {pageId} onClose={() => handleRemovePage(pageId)} />
|
||||
{#each expandedPages as page (page.id)}
|
||||
<SecondaryPage
|
||||
pageId={page.id}
|
||||
onClose={() => handleRemovePage(page.id)}
|
||||
onMinimize={() => handleMinimizePage(page.id)}
|
||||
/>
|
||||
{/each}
|
||||
|
||||
<!-- Neue Seite button (always last in track) -->
|
||||
{#if !editMode}
|
||||
{#if showPagePicker}
|
||||
<PagePicker
|
||||
onSelect={handleAddPage}
|
||||
onClose={() => (showPagePicker = false)}
|
||||
activePageIds={openPages}
|
||||
/>
|
||||
<div bind:this={pagePickerEl}>
|
||||
<PagePicker
|
||||
onSelect={handleAddPage}
|
||||
onClose={() => (showPagePicker = false)}
|
||||
activePageIds={openPages.map((p) => p.id)}
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
<button
|
||||
class="neue-seite-card"
|
||||
|
|
@ -250,6 +320,116 @@
|
|||
background: color-mix(in srgb, var(--color-primary, #8b5cf6) 8%, transparent);
|
||||
}
|
||||
|
||||
/* ── Minimized Tabs ──────────────────────────────────── */
|
||||
.minimized-tabs {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 0.375rem;
|
||||
padding: 0.5rem 1.5rem 0.25rem;
|
||||
overflow-x: auto;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
.minimized-tabs::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.minimized-tab {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.375rem;
|
||||
padding: 0.4rem 0.75rem 0.4rem 0.75rem;
|
||||
background: #fffef5;
|
||||
border: 1px solid rgba(0, 0, 0, 0.08);
|
||||
border-radius: 0.375rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.minimized-tab:hover {
|
||||
background: #fffdf0;
|
||||
border-color: rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
:global(.dark) .minimized-tab {
|
||||
background: #252220;
|
||||
border-color: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
:global(.dark) .minimized-tab:hover {
|
||||
background: #2a2725;
|
||||
border-color: rgba(255, 255, 255, 0.14);
|
||||
}
|
||||
|
||||
.minimized-tab-dot {
|
||||
width: 0.5rem;
|
||||
height: 0.5rem;
|
||||
border-radius: 9999px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.minimized-tab-title {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
color: #6b7280;
|
||||
}
|
||||
:global(.dark) .minimized-tab-title {
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.minimized-tab-close {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: #d1d5db;
|
||||
border-radius: 0.125rem;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
.minimized-tab-close:hover {
|
||||
color: #6b7280;
|
||||
background: rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
:global(.dark) .minimized-tab-close {
|
||||
color: #4b5563;
|
||||
}
|
||||
:global(.dark) .minimized-tab-close:hover {
|
||||
color: #9ca3af;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.minimized-tab-add {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 0.375rem;
|
||||
border: 1px dashed rgba(0, 0, 0, 0.15);
|
||||
background: transparent;
|
||||
color: #9ca3af;
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
.minimized-tab-add:hover {
|
||||
border-color: var(--color-primary, #8b5cf6);
|
||||
color: var(--color-primary, #8b5cf6);
|
||||
background: color-mix(in srgb, var(--color-primary, #8b5cf6) 4%, transparent);
|
||||
}
|
||||
:global(.dark) .minimized-tab-add {
|
||||
border-color: rgba(255, 255, 255, 0.1);
|
||||
color: #4b5563;
|
||||
}
|
||||
:global(.dark) .minimized-tab-add:hover {
|
||||
border-color: var(--color-primary, #8b5cf6);
|
||||
color: var(--color-primary, #8b5cf6);
|
||||
background: color-mix(in srgb, var(--color-primary, #8b5cf6) 8%, transparent);
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue