Compact todo items to single-line rows, update stylesheet
- TodoItem: flatten to single-line row layout — checkbox, title, pills, date/reset info, actions all inline. Use hover:bg-card-elevated instead of bordered card with shadow. - TodoList: tighten spacing from space-y-2 to space-y-0.5 - stylesheet.md: document list-view vs card-view convention — list pages use single-line rows, grid pages use multi-line cards Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
66ad8902d7
commit
cd868bd6ea
359
.claude/context/stylesheet.md
Normal file
359
.claude/context/stylesheet.md
Normal file
@ -0,0 +1,359 @@
|
||||
# UMBRA Design System & Stylesheet
|
||||
|
||||
> This document defines the visual language for every page in UMBRA.
|
||||
> All frontend work MUST conform to these patterns. Do not invent new colors, spacing scales, or component styles.
|
||||
|
||||
---
|
||||
|
||||
## Visual Reference
|
||||
|
||||
The dashboard establishes the canonical look: a near-black canvas with thin-bordered cards, cyan accent highlights, the Sora/DM Sans type pairing, and subtle glow effects on interactive elements. Every page should feel like a natural extension of this dashboard.
|
||||
|
||||
**Key visual characteristics:**
|
||||
- Ultra-dark background (~#0a0a0a) with very subtle card elevation (#0d0d0d cards)
|
||||
- Thin 1px borders in dark gray (#262626) - never heavy or prominent
|
||||
- Accent color (default cyan) used sparingly: active nav, stat icons, today highlights, hover glows
|
||||
- High-contrast white text on dark, with muted gray (#a3a3a3) for secondary content
|
||||
- Cards have a gentle hover glow (`shadow-accent/5`) - not dramatic, just a hint of life
|
||||
- Clean data-dense layouts with compact spacing - no wasted space
|
||||
- Type badges and priority pills use color-coded backgrounds at 10% opacity (e.g. `bg-blue-500/10 text-blue-400`)
|
||||
|
||||
---
|
||||
|
||||
## 1. Color Tokens (CSS Custom Properties)
|
||||
|
||||
All colors use HSL format via CSS variables. Never hardcode hex values.
|
||||
|
||||
### Surface Colors
|
||||
| Token | HSL | Approx Hex | Usage |
|
||||
|---|---|---|---|
|
||||
| `--background` | `0 0% 3.9%` | `#0a0a0a` | Page background |
|
||||
| `--card` | `0 0% 5%` | `#0d0d0d` | Card/panel surfaces |
|
||||
| `--card-elevated` | `0 0% 7%` | `#121212` | Hover states, elevated surfaces |
|
||||
| `--secondary` | `0 0% 10%` | `#1a1a1a` | Secondary backgrounds, muted surfaces |
|
||||
| `--muted` | `0 0% 10%` | `#1a1a1a` | Disabled/muted backgrounds |
|
||||
|
||||
### Text Colors
|
||||
| Token | HSL | Approx Hex | Usage |
|
||||
|---|---|---|---|
|
||||
| `--foreground` | `0 0% 98%` | `#fafafa` | Primary text |
|
||||
| `--muted-foreground` | `0 0% 63.9%` | `#a3a3a3` | Secondary/subtle text |
|
||||
|
||||
### Border & Input
|
||||
| Token | HSL | Approx Hex | Usage |
|
||||
|---|---|---|---|
|
||||
| `--border` | `0 0% 14.9%` | `#262626` | All borders, dividers |
|
||||
| `--input` | `0 0% 14.9%` | `#262626` | Input borders |
|
||||
|
||||
### Semantic Colors
|
||||
| Token | HSL | Usage |
|
||||
|---|---|---|
|
||||
| `--destructive` | `0 62.8% 30.6%` | Delete actions, errors |
|
||||
| `--primary` / `--accent-color` / `--ring` | Dynamic (accent HSL) | Primary actions, focus rings |
|
||||
|
||||
### Accent System (Dynamic)
|
||||
|
||||
The accent is composed of three root variables set at runtime by `useTheme`:
|
||||
- `--accent-h` (hue)
|
||||
- `--accent-s` (saturation)
|
||||
- `--accent-l` (lightness)
|
||||
|
||||
These feed into `--primary`, `--ring`, and `--accent-color`.
|
||||
|
||||
**Presets:**
|
||||
| Name | H | S | L | Approx Color |
|
||||
|---|---|---|---|---|
|
||||
| `cyan` (default) | 187 | 85.7% | 53.3% | Teal-cyan |
|
||||
| `blue` | 217 | 91.2% | 59.8% | Bright blue |
|
||||
| `purple` | 258 | 89.5% | 66.3% | Violet |
|
||||
| `orange` | 21 | 94.6% | 53.3% | Warm orange |
|
||||
| `green` | 142 | 70.6% | 45.3% | Emerald |
|
||||
|
||||
### Semantic Color Assignments (Fixed per feature)
|
||||
|
||||
These are NOT theme-dependent. They provide visual consistency for item types:
|
||||
|
||||
| Feature | Text | Background | Usage |
|
||||
|---|---|---|---|
|
||||
| Todos | `text-blue-400` | `bg-blue-500/10` | Todo badges, type indicators |
|
||||
| Events | `text-purple-400` | `bg-purple-500/10` | Event badges, calendar items |
|
||||
| Reminders | `text-orange-400` | `bg-orange-500/10` | Reminder badges |
|
||||
| Projects | `text-blue-400` | `bg-blue-500/10` | Project counts |
|
||||
| In Progress | `text-purple-400` | `bg-purple-500/10` | Status indicators |
|
||||
| Open Todos | `text-teal-400` | `bg-teal-500/10` | Stat cards |
|
||||
| Weather | `text-amber-400` | `bg-amber-500/10` | Weather widget |
|
||||
| High priority | `text-red-400` | `bg-red-500/10` | Priority pills |
|
||||
| Medium priority | `text-yellow-400` | `bg-yellow-500/10` | Priority pills |
|
||||
| Low priority | `text-green-400` | `bg-green-500/10` | Priority pills |
|
||||
|
||||
---
|
||||
|
||||
## 2. Typography
|
||||
|
||||
### Font Families
|
||||
- **Headings:** `Sora` (`font-heading`) — all h1-h6, card titles, stat values, logo
|
||||
- **Body:** `DM Sans` (`font-body`) — everything else
|
||||
|
||||
### Type Scale (as used)
|
||||
| Class | Usage |
|
||||
|---|---|
|
||||
| `text-[9px]` | Micro labels (priority badges, type tags) |
|
||||
| `text-[10px] tracking-wider uppercase` | Stat labels |
|
||||
| `text-[11px]` | Small metadata |
|
||||
| `text-xs` | Timestamps, secondary metadata |
|
||||
| `text-sm` | Body text, list items, form inputs, nav items |
|
||||
| `text-lg font-semibold` | Card titles (with `font-heading`) |
|
||||
| `text-xl font-bold tabular-nums` | Stat values (with `font-heading`) |
|
||||
| `text-3xl font-bold tracking-tight` | Page greeting/hero (with `font-heading`) |
|
||||
|
||||
### Font Rendering
|
||||
```css
|
||||
font-feature-settings: "rlig" 1, "calt" 1;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Spacing & Layout
|
||||
|
||||
### Border Radius
|
||||
| Token | Value | Usage |
|
||||
|---|---|---|
|
||||
| `rounded-lg` | `0.5rem` (8px) | Cards, panels |
|
||||
| `rounded-md` | `~6px` | Buttons, inputs |
|
||||
| `rounded-sm` | `~4px` | Small elements |
|
||||
| `rounded-full` | Pill | Badges, dots, icon containers |
|
||||
|
||||
### Page Layout
|
||||
```
|
||||
App Shell: flex h-screen overflow-hidden bg-background
|
||||
├── Sidebar: w-64 (expanded) / w-16 (collapsed), border-r, bg-card
|
||||
│ transition-all duration-300
|
||||
└── Content: flex-1 flex flex-col overflow-hidden
|
||||
├── MobileHeader: md:hidden, h-14, border-b, bg-card
|
||||
└── <main>: flex-1 overflow-y-auto
|
||||
└── Page content: px-6 py-6
|
||||
```
|
||||
|
||||
### Grid Patterns
|
||||
| Pattern | Classes | Usage |
|
||||
|---|---|---|
|
||||
| Stat row | `grid gap-2.5 grid-cols-2 lg:grid-cols-4` | Dashboard stats |
|
||||
| Main + sidebar | `grid gap-5 lg:grid-cols-5` → `lg:col-span-3` + `lg:col-span-2` | Dashboard layout |
|
||||
| Card grid | `grid gap-4 md:grid-cols-2 lg:grid-cols-3` | List pages (people, locations, projects) |
|
||||
|
||||
### Standard Spacing
|
||||
- Page padding: `px-6 py-6`
|
||||
- Card padding: `p-5` (header), `p-5 pt-0` (content/footer)
|
||||
- Card gap in grid: `gap-4` to `gap-5`
|
||||
- List row padding: `py-1.5 px-2`
|
||||
- Section gap: `space-y-4` to `space-y-6`
|
||||
|
||||
---
|
||||
|
||||
## 4. Component Patterns
|
||||
|
||||
### Card
|
||||
```
|
||||
Base: rounded-lg border bg-card text-card-foreground shadow-sm
|
||||
Hover: hover:shadow-lg hover:shadow-accent/5 hover:border-accent/20
|
||||
Motion: transition-all duration-200
|
||||
Header: flex flex-col space-y-1.5 p-5
|
||||
Title: font-heading text-lg font-semibold leading-none tracking-tight
|
||||
Content: p-5 pt-0
|
||||
Footer: flex items-center p-5 pt-0
|
||||
```
|
||||
|
||||
### Button (CVA variants)
|
||||
```
|
||||
Base: inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium
|
||||
transition-colors focus-visible:ring-2 focus-visible:ring-ring
|
||||
Variants:
|
||||
default: bg-accent text-accent-foreground hover:bg-accent/90
|
||||
destructive: bg-destructive text-destructive-foreground hover:bg-destructive/90
|
||||
outline: border border-input bg-background hover:bg-accent/10 hover:text-accent
|
||||
secondary: bg-secondary text-secondary-foreground hover:bg-secondary/80
|
||||
ghost: hover:bg-accent/10 hover:text-accent
|
||||
link: text-accent underline-offset-4 hover:underline
|
||||
Sizes:
|
||||
default: h-10 px-4 py-2
|
||||
sm: h-9 px-3
|
||||
lg: h-11 px-8
|
||||
icon: h-10 w-10
|
||||
```
|
||||
|
||||
### Badge
|
||||
```
|
||||
Base: inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold
|
||||
Variants:
|
||||
default: border-transparent bg-accent text-accent-foreground
|
||||
secondary: border-transparent bg-secondary text-secondary-foreground
|
||||
destructive: border-transparent bg-destructive text-destructive-foreground
|
||||
outline: text-foreground
|
||||
```
|
||||
|
||||
### Input / Select / Textarea
|
||||
```
|
||||
h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm
|
||||
placeholder:text-muted-foreground
|
||||
focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2
|
||||
disabled:cursor-not-allowed disabled:opacity-50
|
||||
```
|
||||
|
||||
### Dialog
|
||||
```
|
||||
Overlay: fixed inset-0 bg-background/80 backdrop-blur-sm
|
||||
Content: border bg-card p-6 shadow-lg rounded-lg max-w-xl
|
||||
Header: flex flex-col space-y-1.5
|
||||
Title: text-lg font-semibold leading-none tracking-tight
|
||||
Footer: flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2
|
||||
Close: absolute right-4 top-4, X icon h-4 w-4
|
||||
```
|
||||
|
||||
### Empty State
|
||||
```
|
||||
Container: flex flex-col items-center justify-center py-16 px-4
|
||||
Icon: rounded-full bg-muted p-4 mb-4, icon h-8 w-8 text-muted-foreground
|
||||
Title: text-lg font-semibold mb-1
|
||||
Desc: text-sm text-muted-foreground text-center max-w-sm mb-4
|
||||
Action: <Button> with Plus icon
|
||||
```
|
||||
|
||||
### Skeleton
|
||||
```
|
||||
Base: animate-pulse rounded-md bg-muted
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Widget & Data Patterns
|
||||
|
||||
### Stat Card
|
||||
```
|
||||
Container: Card with bg-gradient-to-br from-accent/[0.03] to-transparent
|
||||
Icon: p-1.5 rounded-md bg-{color}/10
|
||||
Label: text-[10px] tracking-wider uppercase text-muted-foreground
|
||||
Value: font-heading text-xl font-bold tabular-nums
|
||||
```
|
||||
|
||||
### List Row (full-page lists: todos, reminders, etc.)
|
||||
```
|
||||
Container: space-y-0.5 (tight stacking, no card borders)
|
||||
Row: flex items-center gap-3 px-3 py-2 rounded-md
|
||||
hover:bg-card-elevated transition-colors duration-150
|
||||
Layout: Everything on ONE line. Left-to-right:
|
||||
[interactive control] [title truncate flex-1] [pills/badges] [metadata] [actions]
|
||||
Title: text-sm font-medium truncate flex-1 min-w-0
|
||||
Metadata: text-[11px] shrink-0, right-aligned before actions
|
||||
Actions: ghost icon buttons h-7 w-7, last in row
|
||||
```
|
||||
|
||||
**List view vs card view:** If a page shows items as a *list* (todos, upcoming),
|
||||
use single-line rows with `hover:bg-card-elevated` — no borders, no shadow,
|
||||
no multi-line content. If a page shows items as a *grid of cards* (projects,
|
||||
people, locations), use the Card component with `p-5`, multi-line content, and
|
||||
`hover:shadow-lg hover:shadow-accent/5`.
|
||||
|
||||
### Widget List Row (dashboard widgets — more compact)
|
||||
```
|
||||
flex items-center gap-2 py-1.5 px-2 rounded-md
|
||||
hover:bg-card-elevated transition-colors duration-150
|
||||
```
|
||||
|
||||
### Section Header (widget title)
|
||||
```
|
||||
Icon wrapped in p-1.5 rounded-md bg-{color}/10
|
||||
alongside CardTitle
|
||||
"View all" or count label: text-xs text-muted-foreground
|
||||
```
|
||||
|
||||
### Priority Pills
|
||||
```
|
||||
text-[9px] px-1.5 py-0.5 rounded-full font-medium
|
||||
High: bg-red-500/20 text-red-400
|
||||
Medium: bg-yellow-500/20 text-yellow-400
|
||||
Low: bg-green-500/20 text-green-400
|
||||
```
|
||||
|
||||
### Type Tags
|
||||
```
|
||||
text-[9px] px-1.5 py-0.5 rounded font-medium uppercase tracking-wide
|
||||
Todo: bg-blue-500/15 text-blue-400
|
||||
Event: bg-purple-500/15 text-purple-400
|
||||
Reminder: bg-orange-500/15 text-orange-400
|
||||
```
|
||||
|
||||
### Week Timeline (today highlight)
|
||||
```
|
||||
bg-accent/10 border border-accent/30 rounded-lg
|
||||
shadow-[0_0_12px_hsl(var(--accent-color)/0.15)]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Navigation
|
||||
|
||||
### Sidebar Nav Link
|
||||
```
|
||||
Base: flex items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium
|
||||
transition-all duration-200 border-l-2
|
||||
Active: bg-accent/15 text-accent border-accent
|
||||
Inactive: text-muted-foreground hover:bg-accent/10 hover:text-accent border-transparent
|
||||
Icon: h-5 w-5 shrink-0
|
||||
```
|
||||
|
||||
### Logo
|
||||
```
|
||||
font-heading text-xl font-bold tracking-tight text-accent
|
||||
```
|
||||
|
||||
### Logout
|
||||
```
|
||||
hover:bg-destructive/10 hover:text-destructive
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Animations & Transitions
|
||||
|
||||
### Keyframes
|
||||
| Name | Effect | Duration | Easing |
|
||||
|---|---|---|---|
|
||||
| `fade-in` | opacity 0 → 1 | 300ms | ease-out |
|
||||
| `slide-up` | opacity 0→1 + Y(8px→0) | 400ms | ease-out |
|
||||
|
||||
### Staggered Entry (dashboard sections)
|
||||
```
|
||||
animationDelay: '50ms' / '100ms' / '150ms'
|
||||
animationFillMode: 'backwards'
|
||||
```
|
||||
|
||||
### Standard Transitions
|
||||
| Usage | Classes |
|
||||
|---|---|
|
||||
| Interactive elements | `transition-all duration-200` |
|
||||
| Color changes | `transition-colors duration-150` |
|
||||
| Sidebar collapse | `transition-all duration-300` |
|
||||
|
||||
---
|
||||
|
||||
## 8. Scrollbar
|
||||
```css
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: hsl(0 0% 20%) transparent; /* #333 thumb on transparent */
|
||||
::-webkit-scrollbar { width: 6px; height: 6px; }
|
||||
::-webkit-scrollbar-thumb:hover { background: hsl(var(--accent-color) / 0.5); }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Design Principles
|
||||
|
||||
1. **Dark-first:** Everything is designed for the dark palette. There is no light mode.
|
||||
2. **Accent as highlight, not flood:** The accent color draws attention to interactive/important elements. It should never dominate a section.
|
||||
3. **10% opacity backgrounds:** Feature colors (blue, purple, orange, etc.) are always at 10-20% opacity for backgrounds, with 400-level text on top. Never use full-saturation backgrounds.
|
||||
4. **Data density:** Compact rows, small text for metadata, efficient use of space. This is a productivity tool, not a marketing page.
|
||||
5. **Consistent card pattern:** Every distinct content section is a Card. Cards have uniform padding (p-5), title styling, and hover behavior.
|
||||
6. **Typography hierarchy:** Sora for headings/numbers, DM Sans for body. This distinction creates visual rhythm without extra decoration.
|
||||
7. **Subtle motion:** Hover glows, fade-ins, and slide-ups. No bouncing, spinning, or attention-grabbing animations.
|
||||
8. **Icon consistency:** All icons from Lucide React, sized at `h-5 w-5` for nav/actions, `h-4 w-4` for inline, `h-8 w-8` for empty states.
|
||||
@ -71,9 +71,9 @@ export default function TodoItem({ todo, onEdit }: TodoItemProps) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'flex items-center gap-3 rounded-lg border bg-card p-4 transition-all duration-200',
|
||||
'hover:shadow-lg hover:shadow-accent/5 hover:border-accent/20',
|
||||
todo.completed && 'opacity-60'
|
||||
'flex items-center gap-3 px-3 py-2 rounded-md transition-colors duration-150',
|
||||
'hover:bg-card-elevated',
|
||||
todo.completed && 'opacity-50'
|
||||
)}
|
||||
>
|
||||
<Checkbox
|
||||
@ -82,97 +82,85 @@ export default function TodoItem({ todo, onEdit }: TodoItemProps) {
|
||||
disabled={toggleMutation.isPending}
|
||||
/>
|
||||
|
||||
<div className="flex-1 min-w-0 cursor-pointer" onClick={() => onEdit(todo)}>
|
||||
<div className="flex items-center gap-2">
|
||||
<h3
|
||||
className={cn(
|
||||
'font-medium truncate',
|
||||
todo.completed && 'line-through text-muted-foreground'
|
||||
)}
|
||||
>
|
||||
{todo.title}
|
||||
</h3>
|
||||
<span
|
||||
className={cn(
|
||||
'text-[9px] px-1.5 py-0.5 rounded-full font-medium shrink-0',
|
||||
priorityStyles[todo.priority]
|
||||
)}
|
||||
>
|
||||
{todo.priority}
|
||||
</span>
|
||||
{todo.category && (
|
||||
<span className="text-[9px] px-1.5 py-0.5 rounded font-medium uppercase tracking-wide bg-blue-500/15 text-blue-400 shrink-0">
|
||||
{todo.category}
|
||||
</span>
|
||||
)}
|
||||
{todo.recurrence_rule && (
|
||||
<span className="text-[9px] px-1.5 py-0.5 rounded font-medium uppercase tracking-wide bg-purple-500/15 text-purple-400 shrink-0">
|
||||
{recurrenceLabels[todo.recurrence_rule] || todo.recurrence_rule}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{todo.description && (
|
||||
<p className="text-sm text-muted-foreground mt-1 line-clamp-1">{todo.description}</p>
|
||||
<span
|
||||
className={cn(
|
||||
'text-sm font-medium truncate flex-1 min-w-0 cursor-pointer',
|
||||
todo.completed && 'line-through text-muted-foreground'
|
||||
)}
|
||||
onClick={() => onEdit(todo)}
|
||||
>
|
||||
{todo.title}
|
||||
</span>
|
||||
|
||||
<div className="flex items-center gap-3 mt-1.5">
|
||||
{/* Inline pills */}
|
||||
<span
|
||||
className={cn(
|
||||
'text-[9px] px-1.5 py-0.5 rounded-full font-medium shrink-0',
|
||||
priorityStyles[todo.priority]
|
||||
)}
|
||||
>
|
||||
{todo.priority}
|
||||
</span>
|
||||
|
||||
{todo.category && (
|
||||
<span className="text-[9px] px-1.5 py-0.5 rounded font-medium uppercase tracking-wide bg-blue-500/15 text-blue-400 shrink-0">
|
||||
{todo.category}
|
||||
</span>
|
||||
)}
|
||||
|
||||
{todo.recurrence_rule && (
|
||||
<span className="text-[9px] px-1.5 py-0.5 rounded font-medium uppercase tracking-wide bg-purple-500/15 text-purple-400 shrink-0">
|
||||
{recurrenceLabels[todo.recurrence_rule] || todo.recurrence_rule}
|
||||
</span>
|
||||
)}
|
||||
|
||||
{/* Date / time / reset info — right-aligned cluster */}
|
||||
{showResetInfo ? (
|
||||
<div className="flex items-center gap-1 text-[11px] text-purple-400 shrink-0">
|
||||
<RefreshCw className="h-3 w-3" />
|
||||
<span>
|
||||
Resets {format(resetDate, 'EEE dd/MM')}
|
||||
{nextDueDate && (
|
||||
<> · Due {format(nextDueDate, 'dd/MM')}{todo.due_time ? ` ${todo.due_time.slice(0, 5)}` : ''}</>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{dueDate && (
|
||||
<div
|
||||
<span
|
||||
className={cn(
|
||||
'flex items-center gap-1 text-xs',
|
||||
isOverdue
|
||||
? 'text-red-400'
|
||||
: isDueToday
|
||||
? 'text-yellow-400'
|
||||
: 'text-muted-foreground'
|
||||
'flex items-center gap-1 text-[11px] shrink-0',
|
||||
isOverdue ? 'text-red-400' : isDueToday ? 'text-yellow-400' : 'text-muted-foreground'
|
||||
)}
|
||||
>
|
||||
{isOverdue ? (
|
||||
<AlertCircle className="h-3 w-3" />
|
||||
) : (
|
||||
<Calendar className="h-3 w-3" />
|
||||
)}
|
||||
{isOverdue ? 'Overdue — ' : isDueToday ? 'Today — ' : ''}
|
||||
{format(dueDate, 'MMM d, yyyy')}
|
||||
</div>
|
||||
{isOverdue ? <AlertCircle className="h-3 w-3" /> : <Calendar className="h-3 w-3" />}
|
||||
{isOverdue ? 'Overdue · ' : isDueToday ? 'Today · ' : ''}
|
||||
{format(dueDate, 'MMM d')}
|
||||
</span>
|
||||
)}
|
||||
|
||||
{todo.due_time && (
|
||||
<div className="flex items-center gap-1 text-xs text-muted-foreground">
|
||||
<span className="flex items-center gap-1 text-[11px] text-muted-foreground shrink-0">
|
||||
<Clock className="h-3 w-3" />
|
||||
{todo.due_time.slice(0, 5)}
|
||||
</div>
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{showResetInfo && (
|
||||
<div className="flex items-center gap-1 text-xs text-purple-400">
|
||||
<RefreshCw className="h-3 w-3" />
|
||||
<span>
|
||||
Resets {format(resetDate, 'EEE dd/MM/yy')}
|
||||
{nextDueDate && (
|
||||
<> · Next due {format(nextDueDate, 'dd/MM/yy')}{todo.due_time ? ` at ${todo.due_time.slice(0, 5)}` : ''}</>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-1 shrink-0">
|
||||
<Button variant="ghost" size="icon" onClick={() => onEdit(todo)} className="h-8 w-8">
|
||||
<Pencil className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => deleteMutation.mutate()}
|
||||
disabled={deleteMutation.isPending}
|
||||
className="h-8 w-8 hover:bg-destructive/10 hover:text-destructive"
|
||||
>
|
||||
<Trash2 className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
</div>
|
||||
{/* Actions */}
|
||||
<Button variant="ghost" size="icon" onClick={() => onEdit(todo)} className="h-7 w-7 shrink-0">
|
||||
<Pencil className="h-3 w-3" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => deleteMutation.mutate()}
|
||||
disabled={deleteMutation.isPending}
|
||||
className="h-7 w-7 shrink-0 hover:bg-destructive/10 hover:text-destructive"
|
||||
>
|
||||
<Trash2 className="h-3 w-3" />
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -73,7 +73,7 @@ export default function TodoList({ todos, onEdit, onAdd }: TodoListProps) {
|
||||
// If only one group, skip headers
|
||||
if (groups.length === 1) {
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<div className="space-y-0.5">
|
||||
{groups[0].todos.map((todo) => (
|
||||
<TodoItem key={todo.id} todo={todo} onEdit={onEdit} />
|
||||
))}
|
||||
@ -89,7 +89,7 @@ export default function TodoList({ todos, onEdit, onAdd }: TodoListProps) {
|
||||
{group.label}
|
||||
<span className="ml-1.5 tabular-nums">({group.todos.length})</span>
|
||||
</h3>
|
||||
<div className="space-y-2">
|
||||
<div className="space-y-0.5">
|
||||
{group.todos.map((todo) => (
|
||||
<TodoItem key={todo.id} todo={todo} onEdit={onEdit} />
|
||||
))}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user