add more ux and ui patches with extra login helper

This commit is contained in:
JCEEE
2026-08-27 14:13:14 +01:00
parent 84b3cd48f4
commit 9f335db7b2
16 changed files with 643 additions and 150 deletions
@@ -4,6 +4,7 @@
import { famStore } from '$lib/stores/fam.svelte';
import { ViewHeader, CardGrid, Card } from '$lib/components';
import TemplateIcon from '$lib/components/TemplateIcon.svelte';
import { choresIcon, todoIcon } from '$lib/components/icons';
import { formatHumanDate } from '$lib/format';
import { TEMPLATE_COLORS, ICON_NAMES, accentBg, outlineColor } from '$lib/templateIcons';
import type { ChoreTemplate, AssignedChore, Member, Season, Completion } from '$lib/types';
@@ -305,21 +306,12 @@
<p class="error">{form.error}</p>
{/if}
<CardGrid>
<CardGrid stackTablet>
<!-- Templates Section (at top, like bonuses) -->
<Card cols={3}>
<div class="template-section">
<div class="template-header">
<h3>Templates — Drag to assign</h3>
<div class="season-filter">
<label>Season:</label>
<select bind:value={seasonFilter}>
<option value="all">All Chores</option>
{#each activeSeasons as s}
<option value={s.id}>{s.name}</option>
{/each}
</select>
</div>
</div>
<div class="template-list">
{#each templates as t}
@@ -349,11 +341,22 @@
<!-- Member Chore Assignment Kanban (3-column grid) -->
<Card cols={3} scrollX>
<div class="kanban-toolbar">
<div class="season-filter">
<label>Season:</label>
<select bind:value={seasonFilter}>
<option value="all">All Chores</option>
{#each activeSeasons as s}
<option value={s.id}>{s.name}</option>
{/each}
</select>
</div>
</div>
<div class="kanban">
{#each members as m}
<div
class="column member-col"
style="border-top-color: {m.color}"
style="border-top-color: {m.color}; --mc: {m.color}"
ondragover={handleDragOver}
ondrop={(e) => handleDrop(e, m.id)}
>
@@ -363,13 +366,17 @@
</h3>
<!-- TODOS accordion wrapper -->
<div class="accordion-wrapper accordion-todos">
<div class="accordion-wrapper accordion-todos" class:open={accordionState[m.id]?.todos ?? true}>
<button class="accordion-head" onclick={() => toggleAccordion(m.id, 'todos')}>
<span class="accordion-label">📋 Todos ({todosForMember(m.id).length})</span>
<span class="accordion-title">
<span class="accordion-icon">{@html todoIcon}</span>
<span class="accordion-label">Todos</span>
</span>
<span class="accordion-chevron"
>{@html (accordionState[m.id]?.todos ?? true) ? CHEVRON_UP : CHEVRON_DOWN}</span
>
</button>
<span class="accordion-count" style="background:{m.color}">{todosForMember(m.id).length}</span>
{#if accordionState[m.id]?.todos ?? true}
<div class="accordion-body">
{#each sortedTodosForMember(m.id) as a}
@@ -433,13 +440,17 @@
</div>
<!-- CHORES accordion wrapper -->
<div class="accordion-wrapper accordion-chores">
<div class="accordion-wrapper accordion-chores" class:open={accordionState[m.id]?.chores ?? true}>
<button class="accordion-head" onclick={() => toggleAccordion(m.id, 'chores')}>
<span class="accordion-label">Chores ({assignedForMember(m.id).length})</span>
<span class="accordion-title">
<span class="accordion-icon">{@html choresIcon}</span>
<span class="accordion-label">Chores</span>
</span>
<span class="accordion-chevron"
>{@html (accordionState[m.id]?.chores ?? true) ? CHEVRON_UP : CHEVRON_DOWN}</span
>
</button>
<span class="accordion-count" style="background:{m.color}">{assignedForMember(m.id).length}</span>
{#if accordionState[m.id]?.chores ?? true}
<div class="accordion-body">
{#each assignedForMember(m.id) as a}
@@ -455,11 +466,8 @@
<TemplateIcon name={a.icon} size={16} color={outlineColor(a.color)} />
<strong>{a.customName || tName}</strong>
<span class="badge">{a.frequency}</span>
<span class="badge type">{a.type}</span>
{#each a.seasonIds || [] as sid}
<span class="badge season" style="border-color:{seasonColor(sid)}; color:{seasonColor(sid)}"
>{seasonName(sid)}</span
>
<span class="season-dot" title={seasonName(sid)} style="background:{seasonColor(sid)}"></span>
{/each}
{#if (a.seasonIds || []).length === 0}
<span class="badge season muted">All year</span>
@@ -874,6 +882,14 @@
align-items: center;
gap: 0.4rem;
}
@media (max-width: 1023px) {
.template-list {
flex-direction: column;
}
.template-list .card.template {
width: 100%;
}
}
/* ── Kanban (3 columns, scrolls via Card scrollX) ── */
.kanban {
@@ -1038,6 +1054,11 @@
align-items: center;
gap: 0.5rem;
}
.kanban-toolbar {
display: flex;
align-items: center;
margin-bottom: 0.75rem;
}
.season-filter label {
font-size: 0.8rem;
color: #6b7280;
@@ -1059,6 +1080,14 @@
color: #9ca3af;
border-color: #e5e7eb;
}
.season-dot {
display: inline-block;
width: 12px;
height: 12px;
border-radius: 50%;
flex: none;
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.12);
}
.card-meta {
font-size: 0.7rem;
color: #9ca3af;
@@ -1234,37 +1263,45 @@
/* ── Accordion wrappers ── */
.accordion-wrapper {
background: #6366f1;
border-radius: 10px;
position: relative;
margin: 0.75rem 0 0.3rem;
overflow: hidden;
box-shadow: 0 2px 6px rgba(99, 102, 241, 0.25);
}
.accordion-wrapper.accordion-todos {
background: #2e8b57;
box-shadow: 0 2px 6px rgba(46, 139, 87, 0.25);
.accordion-wrapper:first-child {
margin-top: 1.1rem;
}
.accordion-head {
width: 100%;
padding: 0.55rem 0.85rem;
border: none;
border-left: 4px solid rgba(0, 0, 0, 0.2);
background: transparent;
background: var(--mc, #6366f1);
border-radius: 10px;
cursor: pointer;
font-size: 0.82rem;
font-weight: 700;
color: #fff;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
display: flex;
justify-content: space-between;
align-items: center;
transition: filter 0.15s;
}
.accordion-wrapper.accordion-todos .accordion-head {
background: oklch(from var(--mc, #6366f1) calc(l + 0.08) c h);
}
.accordion-wrapper.open .accordion-head {
border-radius: 10px 10px 0 0;
}
.accordion-head:hover {
filter: brightness(1.1);
}
.accordion-body {
padding: 0.5rem 0.65rem 0.65rem;
background: rgba(0, 0, 0, 0.08);
background: oklch(from var(--mc, #6366f1) calc(l - 0.1) c h);
border-radius: 0 0 10px 10px;
}
.accordion-wrapper.accordion-todos .accordion-body {
background: oklch(from var(--mc, #6366f1) calc(l - 0.02) c h);
}
.accordion-chevron {
display: flex;
@@ -1272,6 +1309,40 @@
flex-shrink: 0;
opacity: 0.8;
}
.accordion-icon {
display: inline-flex;
align-items: center;
}
.accordion-title {
display: flex;
align-items: center;
gap: 0.4rem;
}
.accordion-label {
font-size: 0.82rem;
font-weight: 700;
color: #fff;
}
.accordion-count {
position: absolute;
top: 0;
left: 50%;
transform: translate(-50%, -50%);
min-width: 22px;
height: 22px;
padding: 0 6px;
border-radius: 999px;
background: #6366f1;
color: #fff;
font-size: 0.72rem;
font-weight: 700;
display: flex;
align-items: center;
justify-content: center;
border: 2px solid #fff;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
z-index: 2;
}
/* ── Todo admin cards ── */
.todo-admin-card {