Files
task-manager/client/src/components/adhd/settings/ADHDSettingsPanel.tsx
T
Paul Nothaft 7ce4f7efdc
continuous-integration/drone/push Build is passing
feat: Add Focus Tools (ADHD-friendly productivity features)
- Add Focus Tools dashboard with collapsible help section
- Implement Quick Wins page for tasks under 15 minutes
- Add Single Task Focus mode to reduce overwhelm
- Create Body Doubling page for virtual co-working
- Add visual timer, break reminders, and energy tracking
- Implement hyperfocus protection alerts
- Add ADHD settings panel with customizable options
- Include full English and German translations
- Fix larger touch targets CSS to not break button layouts
- Add Playwright tests for Focus Tools features
2026-01-15 21:20:04 +01:00

298 lines
11 KiB
TypeScript

import React from 'react';
import { useADHDMode, defaultADHDSettings } from '../providers/ADHDModeProvider';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Switch } from '@/components/ui/switch';
import { Slider } from '@/components/ui/slider';
import { Label } from '@/components/ui/label';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import { Button } from '@/components/ui/button';
import {
Brain,
Timer,
Sparkles,
Coffee,
Target,
Battery,
Zap,
Shield,
RotateCcw,
} from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { ADHDModeToggle } from '../ADHDModeToggle';
export function ADHDSettingsPanel() {
const { t } = useTranslation();
const { isEnabled, settings, updateSettings } = useADHDMode();
const handleReset = () => {
updateSettings(defaultADHDSettings);
};
return (
<div className="space-y-6">
{/* Main Toggle */}
<ADHDModeToggle showLabel={true} />
{/* Settings (only shown when enabled) */}
{isEnabled && (
<>
{/* Visual & Interaction Settings */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg">
<Target className="h-5 w-5" />
{t('adhd.settings.visualTitle')}
</CardTitle>
<CardDescription>
{t('adhd.settings.visualDesc')}
</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
{/* Reduced Animations */}
<div className="flex items-center justify-between">
<div className="space-y-0.5">
<Label className="text-base">{t('adhd.settings.reducedAnimations')}</Label>
<p className="text-sm text-muted-foreground">
{t('adhd.settings.reducedAnimationsDesc')}
</p>
</div>
<Switch
checked={settings.reducedAnimations}
onCheckedChange={(checked) => updateSettings({ reducedAnimations: checked })}
/>
</div>
{/* Larger Touch Targets */}
<div className="flex items-center justify-between">
<div className="space-y-0.5">
<Label className="text-base">{t('adhd.settings.largerTargets')}</Label>
<p className="text-sm text-muted-foreground">
{t('adhd.settings.largerTargetsDesc')}
</p>
</div>
<Switch
checked={settings.largerTargets}
onCheckedChange={(checked) => updateSettings({ largerTargets: checked })}
/>
</div>
{/* Single Task Mode Default */}
<div className="flex items-center justify-between">
<div className="space-y-0.5">
<Label className="text-base">{t('adhd.settings.singleTaskDefault')}</Label>
<p className="text-sm text-muted-foreground">
{t('adhd.settings.singleTaskDefaultDesc')}
</p>
</div>
<Switch
checked={settings.singleTaskModeDefault}
onCheckedChange={(checked) => updateSettings({ singleTaskModeDefault: checked })}
/>
</div>
</CardContent>
</Card>
{/* Timer & Focus Settings */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg">
<Timer className="h-5 w-5" />
{t('adhd.settings.timerTitle')}
</CardTitle>
<CardDescription>
{t('adhd.settings.timerDesc')}
</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
{/* Visual Timer */}
<div className="flex items-center justify-between">
<div className="space-y-0.5">
<Label className="text-base">{t('adhd.settings.visualTimer')}</Label>
<p className="text-sm text-muted-foreground">
{t('adhd.settings.visualTimerDesc')}
</p>
</div>
<Switch
checked={settings.visualTimerEnabled}
onCheckedChange={(checked) => updateSettings({ visualTimerEnabled: checked })}
/>
</div>
{/* Hyperfocus Protection */}
<div className="flex items-center justify-between">
<div className="space-y-0.5">
<Label className="text-base flex items-center gap-2">
<Shield className="h-4 w-4" />
{t('adhd.settings.hyperfocusProtection')}
</Label>
<p className="text-sm text-muted-foreground">
{t('adhd.settings.hyperfocusProtectionDesc')}
</p>
</div>
<Switch
checked={settings.hyperfocusProtection}
onCheckedChange={(checked) => updateSettings({ hyperfocusProtection: checked })}
/>
</div>
{/* Hyperfocus Max Minutes */}
{settings.hyperfocusProtection && (
<div className="space-y-3">
<div className="flex items-center justify-between">
<Label>{t('adhd.settings.hyperfocusMaxMinutes')}</Label>
<span className="text-sm text-muted-foreground">
{settings.hyperfocusMaxMinutes} min
</span>
</div>
<Slider
value={[settings.hyperfocusMaxMinutes]}
onValueChange={([value]) => updateSettings({ hyperfocusMaxMinutes: value })}
min={30}
max={180}
step={15}
className="w-full"
/>
</div>
)}
</CardContent>
</Card>
{/* Break Reminder Settings */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg">
<Coffee className="h-5 w-5" />
{t('adhd.settings.breakTitle')}
</CardTitle>
<CardDescription>
{t('adhd.settings.breakDesc')}
</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
{/* Break Reminder Interval */}
<div className="space-y-3">
<div className="flex items-center justify-between">
<Label>{t('adhd.settings.breakInterval')}</Label>
<span className="text-sm text-muted-foreground">
{settings.breakReminderInterval} min
</span>
</div>
<Slider
value={[settings.breakReminderInterval]}
onValueChange={([value]) => updateSettings({ breakReminderInterval: value })}
min={15}
max={90}
step={5}
className="w-full"
/>
</div>
</CardContent>
</Card>
{/* Motivation Settings */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg">
<Sparkles className="h-5 w-5" />
{t('adhd.settings.motivationTitle')}
</CardTitle>
<CardDescription>
{t('adhd.settings.motivationDesc')}
</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
{/* Positive Messaging Level */}
<div className="space-y-2">
<Label>{t('adhd.settings.messagingLevel')}</Label>
<Select
value={settings.positiveMessagingLevel}
onValueChange={(value: 'minimal' | 'normal' | 'high') =>
updateSettings({ positiveMessagingLevel: value })
}
>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="minimal">{t('adhd.settings.messagingMinimal')}</SelectItem>
<SelectItem value="normal">{t('adhd.settings.messagingNormal')}</SelectItem>
<SelectItem value="high">{t('adhd.settings.messagingHigh')}</SelectItem>
</SelectContent>
</Select>
<p className="text-xs text-muted-foreground">
{t('adhd.settings.messagingLevelDesc')}
</p>
</div>
{/* Quick Win Threshold */}
<div className="space-y-3">
<div className="flex items-center justify-between">
<Label className="flex items-center gap-2">
<Zap className="h-4 w-4 text-yellow-500" />
{t('adhd.settings.quickWinThreshold')}
</Label>
<span className="text-sm text-muted-foreground">
{settings.quickWinThreshold} min
</span>
</div>
<Slider
value={[settings.quickWinThreshold]}
onValueChange={([value]) => updateSettings({ quickWinThreshold: value })}
min={5}
max={30}
step={5}
className="w-full"
/>
<p className="text-xs text-muted-foreground">
{t('adhd.settings.quickWinThresholdDesc')}
</p>
</div>
</CardContent>
</Card>
{/* Energy Check-ins */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg">
<Battery className="h-5 w-5" />
{t('adhd.settings.energyTitle')}
</CardTitle>
<CardDescription>
{t('adhd.settings.energyDesc')}
</CardDescription>
</CardHeader>
<CardContent>
<div className="flex items-center justify-between">
<div className="space-y-0.5">
<Label className="text-base">{t('adhd.settings.energyCheckIns')}</Label>
<p className="text-sm text-muted-foreground">
{t('adhd.settings.energyCheckInsDesc')}
</p>
</div>
<Switch
checked={settings.energyCheckInsEnabled}
onCheckedChange={(checked) => updateSettings({ energyCheckInsEnabled: checked })}
/>
</div>
</CardContent>
</Card>
{/* Reset Button */}
<div className="flex justify-end">
<Button variant="outline" onClick={handleReset}>
<RotateCcw className="h-4 w-4 mr-2" />
{t('adhd.settings.reset')}
</Button>
</div>
</>
)}
</div>
);
}