diff --git a/backend/alembic/versions/003_add_preferred_name.py b/backend/alembic/versions/003_add_preferred_name.py new file mode 100644 index 0000000..06fe739 --- /dev/null +++ b/backend/alembic/versions/003_add_preferred_name.py @@ -0,0 +1,26 @@ +"""Add preferred_name to settings + +Revision ID: 003 +Revises: 002 +Create Date: 2026-02-20 00:00:00.000000 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = '003' +down_revision: Union[str, None] = '002' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + op.add_column('settings', sa.Column('preferred_name', sa.String(100), nullable=True)) + + +def downgrade() -> None: + op.drop_column('settings', 'preferred_name') diff --git a/backend/app/models/settings.py b/backend/app/models/settings.py index 4a2c605..2fd432e 100644 --- a/backend/app/models/settings.py +++ b/backend/app/models/settings.py @@ -11,5 +11,6 @@ class Settings(Base): pin_hash: Mapped[str] = mapped_column(String(255), nullable=False) accent_color: Mapped[str] = mapped_column(String(20), default="cyan") upcoming_days: Mapped[int] = mapped_column(Integer, default=7) + preferred_name: Mapped[str | None] = mapped_column(String(100), nullable=True, default=None) created_at: Mapped[datetime] = mapped_column(default=func.now()) updated_at: Mapped[datetime] = mapped_column(default=func.now(), onupdate=func.now()) diff --git a/backend/app/schemas/settings.py b/backend/app/schemas/settings.py index a94fa8b..5556c48 100644 --- a/backend/app/schemas/settings.py +++ b/backend/app/schemas/settings.py @@ -25,12 +25,14 @@ class SettingsCreate(BaseModel): class SettingsUpdate(BaseModel): accent_color: Optional[AccentColor] = None upcoming_days: int | None = None + preferred_name: str | None = None class SettingsResponse(BaseModel): id: int accent_color: str upcoming_days: int + preferred_name: str | None = None created_at: datetime updated_at: datetime diff --git a/frontend/src/components/dashboard/DashboardPage.tsx b/frontend/src/components/dashboard/DashboardPage.tsx index 16ac44d..5567f13 100644 --- a/frontend/src/components/dashboard/DashboardPage.tsx +++ b/frontend/src/components/dashboard/DashboardPage.tsx @@ -9,16 +9,18 @@ import TodoWidget from './TodoWidget'; import CalendarWidget from './CalendarWidget'; import UpcomingWidget from './UpcomingWidget'; import WeekTimeline from './WeekTimeline'; +import DayBriefing from './DayBriefing'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { DashboardSkeleton } from '@/components/ui/skeleton'; -function getGreeting(): string { +function getGreeting(name?: string): string { const hour = new Date().getHours(); - if (hour < 5) return 'Good night.'; - if (hour < 12) return 'Good morning.'; - if (hour < 17) return 'Good afternoon.'; - if (hour < 21) return 'Good evening.'; - return 'Good night.'; + const suffix = name ? `, ${name}.` : '.'; + if (hour < 5) return `Good night${suffix}`; + if (hour < 12) return `Good morning${suffix}`; + if (hour < 17) return `Good afternoon${suffix}`; + if (hour < 21) return `Good evening${suffix}`; + return `Good night${suffix}`; } export default function DashboardPage() { @@ -72,7 +74,7 @@ export default function DashboardPage() { {/* Header — greeting + date */}
{format(new Date(), 'EEEE, MMMM d, yyyy')} @@ -88,6 +90,11 @@ export default function DashboardPage() {
+ {briefing} +
+ ); +} diff --git a/frontend/src/components/dashboard/WeekTimeline.tsx b/frontend/src/components/dashboard/WeekTimeline.tsx index f2e1252..d19f988 100644 --- a/frontend/src/components/dashboard/WeekTimeline.tsx +++ b/frontend/src/components/dashboard/WeekTimeline.tsx @@ -72,8 +72,9 @@ export default function WeekTimeline({ items }: WeekTimelineProps) { key={`${item.type}-${item.id}`} className={cn( 'w-1.5 h-1.5 rounded-full', - typeColors[item.type] || 'bg-muted-foreground' + !item.color && (typeColors[item.type] || 'bg-muted-foreground') )} + style={item.color ? { backgroundColor: item.color } : undefined} /> ))} {day.items.length > 4 && ( diff --git a/frontend/src/components/settings/SettingsPage.tsx b/frontend/src/components/settings/SettingsPage.tsx index 65979dc..6cd8a46 100644 --- a/frontend/src/components/settings/SettingsPage.tsx +++ b/frontend/src/components/settings/SettingsPage.tsx @@ -20,12 +20,24 @@ export default function SettingsPage() { const { settings, updateSettings, changePin, isUpdating, isChangingPin } = useSettings(); const [selectedColor, setSelectedColor] = useState(settings?.accent_color || 'cyan'); const [upcomingDays, setUpcomingDays] = useState(settings?.upcoming_days || 7); + const [preferredName, setPreferredName] = useState(settings?.preferred_name || ''); const [pinForm, setPinForm] = useState({ oldPin: '', newPin: '', confirmPin: '', }); + const handleNameSave = async () => { + const trimmed = preferredName.trim(); + if (trimmed === (settings?.preferred_name || '')) return; + try { + await updateSettings({ preferred_name: trimmed || null }); + toast.success('Name updated'); + } catch (error) { + toast.error('Failed to update name'); + } + }; + const handleColorChange = async (color: string) => { setSelectedColor(color); try { @@ -73,6 +85,34 @@ export default function SettingsPage() {+ Used in the dashboard greeting, e.g. "Good morning, {preferredName || 'Kyle'}." +
+