feat: Notifications page, Sidebar layout fixes, and Achievements enhancements
continuous-integration/drone/push Build is failing
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:
@@ -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 */}
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user