feat: Notifications page, Sidebar layout fixes, and Achievements enhancements
continuous-integration/drone/push Build is failing

- implemented /notifications page with overdue/upcoming alerts
- Fixed sidebar scrolling in collapsed mode
- Moved notification button to sidebar footer
- Enhanced Achievements page with streak stats and tooltips
- Improved XP history to show task titles
- Added missing translations (en/de)
- Removed top bar header
- Fixed Docker environment routing
This commit is contained in:
2025-12-17 10:06:36 +01:00
parent 7b79015ac2
commit 9819d8db0b
47 changed files with 2309 additions and 1241 deletions
+40 -8
View File
@@ -6,7 +6,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog';
import { Globe, Tag, Plus, Edit, Trash2, Layers, User as UserIcon, ShieldCheck, Share2, Server, Copy, Settings as SettingsIcon, Bell } from 'lucide-react';
import { Globe, Tag, Plus, Edit, Trash2, Layers, User as UserIcon, ShieldCheck, Share2, Server, Copy, Settings as SettingsIcon, Bell, Loader2 } from 'lucide-react';
import { Switch } from '@/components/ui/switch';
import { ShareAccessModal } from '@/components/ShareAccessModal';
import { ChangePasswordModal } from '@/components/ChangePasswordModal';
@@ -18,7 +18,7 @@ import { queryClient, apiRequest } from '@/lib/queryClient';
import { useToast } from '@/hooks/use-toast';
import { useLocation } from "wouter";
import { useNotifications } from '@/hooks/use-notifications';
import { DataExportCard } from '@/components/user/DataExportCard';
// import { DataExportCard } from '@/components/user/DataExportCard';
const NotificationSettings = () => {
const { t } = useTranslation();
@@ -62,10 +62,29 @@ export default function Settings({ onNavigateToTemplates }: SettingsProps) {
const [, setLocation] = useLocation();
// Fetch user
const { data: user } = useQuery<User>({
const { data: user, isLoading: isLoadingUser } = useQuery<User>({
queryKey: ['/api/user']
});
if (isLoadingUser) {
return (
<div className="flex items-center justify-center min-h-[50vh]">
<Loader2 className="h-8 w-8 animate-spin text-primary" />
</div>
);
}
if (!user) {
return (
<div className="flex flex-col items-center justify-center min-h-[50vh] space-y-4">
<h2 className="text-2xl font-bold">{t('common.loginRequired')}</h2>
<Button onClick={() => setLocation('/')}>
{t('auth.login')}
</Button>
</div>
);
}
const [isLabelDialogOpen, setIsLabelDialogOpen] = useState(false);
const [editingLabel, setEditingLabel] = useState<Label | null>(null);
const [labelName, setLabelName] = useState('');
@@ -79,6 +98,8 @@ export default function Settings({ onNavigateToTemplates }: SettingsProps) {
const handleLanguageChange = (value: string) => {
i18n.changeLanguage(value);
localStorage.setItem('taskflow-language', value);
// Persist to DB
privacyMutation.mutate({ language: value } as any);
console.log('Language changed to:', value);
};
@@ -98,9 +119,10 @@ export default function Settings({ onNavigateToTemplates }: SettingsProps) {
};
// Fetch labels
const { data: labels = [], isLoading: labelsLoading } = useQuery<Label[]>({
const { data: labelsData, isLoading: labelsLoading } = useQuery<Label[]>({
queryKey: ['/api/labels']
});
const labels = labelsData ?? [];
// Create label mutation
const createLabelMutation = useMutation({
@@ -296,7 +318,7 @@ export default function Settings({ onNavigateToTemplates }: SettingsProps) {
<p className="text-sm text-muted-foreground">{t('settings.social.publicLeaderboardDesc')}</p>
</div>
<Switch
checked={user?.showOnLeaderboard}
checked={!!user?.showOnLeaderboard}
onCheckedChange={(checked) => handlePrivacyUpdate({ showOnLeaderboard: checked })}
/>
</div>
@@ -306,7 +328,7 @@ export default function Settings({ onNavigateToTemplates }: SettingsProps) {
<p className="text-sm text-muted-foreground">{t('settings.social.searchableDesc')}</p>
</div>
<Switch
checked={user?.isSearchable}
checked={!!user?.isSearchable}
onCheckedChange={(checked) => handlePrivacyUpdate({ isSearchable: checked })}
/>
</div>
@@ -316,10 +338,20 @@ export default function Settings({ onNavigateToTemplates }: SettingsProps) {
<p className="text-sm text-muted-foreground">{t('settings.ai.enableUserDesc')}</p>
</div>
<Switch
checked={user?.aiEnabled}
checked={!!user?.aiEnabled}
onCheckedChange={(checked) => handlePrivacyUpdate({ aiEnabled: checked })}
/>
</div>
<div className="flex items-center justify-between">
<div className="space-y-0.5">
<p className="font-medium">{t('settings.social.2fa')}</p>
<p className="text-sm text-muted-foreground">{t('settings.social.2faDesc')}</p>
</div>
<Switch
checked={!!user?.is2faEnabled}
onCheckedChange={(checked) => handlePrivacyUpdate({ is2faEnabled: checked } as any)}
/>
</div>
<div className="pt-2">
<Button variant="outline" onClick={() => setIsShareAccessOpen(true)}>
<Share2 className="w-4 h-4 mr-2" />
@@ -554,7 +586,7 @@ export default function Settings({ onNavigateToTemplates }: SettingsProps) {
</Card>
{/* Data Export */}
<DataExportCard user={user} />
{/* <DataExportCard user={user} /> */}
{/* Admin Section */}
{