Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions src/components/layout/bottom-bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const NAV_ITEMS = [
];

const buttonBaseClass =
'bg-card/95 backdrop-blur flex flex-1 items-center justify-center gap-2 rounded-2xl px-4 py-3 text-sm font-semibold shadow-lg shadow-black/10 ring-1 ring-border/40 transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40 hover:translate-y-[-1px]';
'bg-card/95 backdrop-blur flex min-w-0 flex-1 items-center justify-center gap-1.5 rounded-2xl px-2 py-3 text-sm font-semibold shadow-lg shadow-black/10 ring-1 ring-border/40 transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40 hover:translate-y-[-1px] sm:gap-2 sm:px-4';

export default function BottomBar() {
const pathname = usePathname();
Expand All @@ -34,7 +34,7 @@ export default function BottomBar() {
paddingBottom: 'calc(env(safe-area-inset-bottom, 0px) + 0.75rem)'
}}
>
<div className='pointer-events-auto mx-auto flex w-full max-w-md gap-3 px-4 pb-2 sm:max-w-lg'>
<div className='pointer-events-auto mx-auto flex w-full max-w-md gap-2 px-4 pb-2 sm:max-w-lg sm:gap-3'>
{NAV_ITEMS.map((item) => {
const Icon = item.icon;
const isActive = pathname.startsWith(item.href);
Expand All @@ -50,8 +50,8 @@ export default function BottomBar() {
'border-primary/40 bg-primary text-primary-foreground shadow-lg'
)}
>
<Icon className='h-4 w-4' />
<span>{item.label}</span>
<Icon className='h-4 w-4 shrink-0' />
<span className='truncate'>{item.label}</span>
</Link>
);
})}
Expand All @@ -72,8 +72,8 @@ function AgentButton() {
onClick={() => setOpen(true)}
className={cn(buttonBaseClass, 'text-primary hover:text-primary')}
>
<Sparkles className='h-4 w-4' />
<span>Assistant</span>
<Sparkles className='h-4 w-4 shrink-0' />
<span className='truncate'>Assistant</span>
</button>
<VoiceAgentPanel open={open} onOpenChange={setOpen} agent={agent} />
</>
Expand Down
7 changes: 5 additions & 2 deletions src/components/layout/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,16 @@ export default function Header() {

<div className='min-w-0 flex-1' />

<div className='flex items-center gap-2'>
<div className='flex shrink-0 items-center gap-2'>
<div className='hidden md:flex'>
<SearchInput />
</div>
<UserNav />
<ModeToggle />
<ThemeSelector />
{/* Too wide for phone headers; theme switching stays a desktop affordance. */}
<div className='hidden md:block'>
<ThemeSelector />
</div>
</div>
</header>
);
Expand Down
133 changes: 85 additions & 48 deletions src/features/daily-record/components/daily-mood-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,16 @@ import {
DialogDescription
} from '@/components/ui/dialog';
import { Button } from '@/components/ui/button';
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
import { Label } from '@/components/ui/label';
import { Checkbox } from '@/components/ui/checkbox';
import { CloudRain, CloudSun, Sun } from 'lucide-react';
import { cn } from '@/lib/utils';
import { createClient } from '@/lib/supabase/client';
import { getLocalDayRange } from '@/lib/timezone';
import { TablesInsert, Tables } from '@/types/supabase';

const DAY_QUALITY_OPTIONS = [
{ value: 'good', label: 'Good day' },
{ value: 'bad', label: 'Bad day' },
{ value: 'so-so', label: 'Just so so' }
{ value: 'good', label: 'Good day', icon: Sun },
{ value: 'so-so', label: 'Just so so', icon: CloudSun },
{ value: 'bad', label: 'Bad day', icon: CloudRain }
];

const EMOTION_OPTIONS = ['Happy', 'Anxious', 'Anger', 'Sadness', 'Despair'];
Expand Down Expand Up @@ -166,67 +165,105 @@ const DailyMoodModal = forwardRef<DailyMoodModalRef, DailyMoodModalProps>(

const isValid = dayQuality.length > 0;

const today = new Date().toLocaleDateString('en-US', {
weekday: 'long',
month: 'long',
day: 'numeric'
});

return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogContent className='sm:max-w-md'>
<DialogHeader>
<DialogTitle>Daily Check-in</DialogTitle>
<DialogDescription>
How was your day today? Please share your thoughts with us.
</DialogDescription>
<DialogContent className='rounded-xl sm:max-w-md'>
<DialogHeader className='space-y-1.5'>
<DialogTitle className='font-serif text-2xl font-medium tracking-tight'>
Daily Check-in
</DialogTitle>
<DialogDescription>{today}. Take five seconds.</DialogDescription>
</DialogHeader>

<div className='space-y-6'>
{/* Question 1: Day Quality */}
<div className='space-y-3'>
<Label className='text-sm font-medium'>How was your day?</Label>
<RadioGroup value={dayQuality} onValueChange={setDayQuality}>
{DAY_QUALITY_OPTIONS.map((option) => (
<div
key={option.value}
className='flex items-center space-x-2'
>
<RadioGroupItem value={option.value} id={option.value} />
<Label htmlFor={option.value} className='cursor-pointer'>
{option.label}
</Label>
</div>
))}
</RadioGroup>
<div
className='space-y-3'
role='radiogroup'
aria-label='How was your day?'
>
<p className='text-sm font-medium'>How was your day?</p>
<div className='grid grid-cols-3 gap-2'>
{DAY_QUALITY_OPTIONS.map((option) => {
const Icon = option.icon;
const selected = dayQuality === option.value;
return (
<button
key={option.value}
type='button'
role='radio'
aria-checked={selected}
onClick={() => setDayQuality(option.value)}
className={cn(
'flex flex-col items-center gap-2 rounded-xl border px-2 py-4 text-sm transition-all active:scale-[0.98]',
selected
? 'border-primary/50 bg-accent text-accent-foreground ring-primary/30 font-medium ring-2'
: 'border-border bg-card text-muted-foreground hover:border-primary/30 hover:text-foreground'
)}
>
<Icon className='h-5 w-5' />
<span>{option.label}</span>
</button>
);
})}
</div>
</div>

{/* Question 2: Emotions */}
<div className='space-y-3'>
<Label className='text-sm font-medium'>
How do you feel today?
</Label>
<div className='space-y-2'>
{EMOTION_OPTIONS.map((emotion) => (
<div key={emotion} className='flex items-center space-x-2'>
<Checkbox
id={emotion}
checked={selectedEmotions.includes(emotion)}
onCheckedChange={(checked) =>
handleEmotionChange(emotion, checked as boolean)
}
/>
<Label htmlFor={emotion} className='cursor-pointer'>
<div
className='space-y-3'
role='group'
aria-label='How do you feel today?'
>
<p className='text-sm font-medium'>
How do you feel today?{' '}
<span className='text-muted-foreground font-normal'>
Pick any.
</span>
</p>
<div className='flex flex-wrap gap-2'>
{EMOTION_OPTIONS.map((emotion) => {
const selected = selectedEmotions.includes(emotion);
return (
<button
key={emotion}
type='button'
aria-pressed={selected}
onClick={() => handleEmotionChange(emotion, !selected)}
className={cn(
'rounded-full border px-4 py-1.5 text-sm transition-all active:scale-[0.97]',
selected
? 'border-primary/50 bg-primary text-primary-foreground'
: 'border-border bg-card text-muted-foreground hover:border-primary/30 hover:text-foreground'
)}
>
{emotion}
</Label>
</div>
))}
</button>
);
})}
</div>
</div>

<div className='flex justify-end space-x-2'>
<div className='flex items-center justify-end gap-2 pt-1'>
<Button
variant='outline'
variant='ghost'
className='rounded-full'
onClick={() => setIsOpen(false)}
disabled={isLoading}
>
Skip
</Button>
<Button onClick={handleSubmit} disabled={!isValid || isLoading}>
<Button
className='rounded-full px-6'
onClick={handleSubmit}
disabled={!isValid || isLoading}
>
{isLoading
? isUpdateMode
? 'Updating...'
Expand Down
Loading