feat: add social features, leaderboard, auth enhancements, and admin fixes
continuous-integration/drone/push Build is passing
continuous-integration/drone/push Build is passing
- Implement Social Features: Shared Tasks, Global Access, Privacy Settings (Leaderboard/Searchable). - Add Leaderboard Page and API. - Enhance Auth: Support Email/Username login, explicit duplicate registration errors. - Fix: Admin login password hash regression. - Refactor: Move to wouter for routing, add Admin Dashboard and User Management. - Add Setup Wizard. - Update UI with Sidebar and Gamification elements.
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { Command } from "cmdk";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
Calculator,
|
||||
Calendar,
|
||||
CreditCard,
|
||||
Settings,
|
||||
Smile,
|
||||
User,
|
||||
LayoutGrid,
|
||||
List,
|
||||
CheckSquare,
|
||||
Plus,
|
||||
Moon,
|
||||
Sun,
|
||||
Search
|
||||
} from "lucide-react";
|
||||
import { useTheme } from "next-themes";
|
||||
import { Dialog, DialogContent } from "@/components/ui/dialog";
|
||||
|
||||
interface CommandPaletteProps {
|
||||
onNavigate: (tab: string) => void;
|
||||
onCreateTask: () => void;
|
||||
}
|
||||
|
||||
export function CommandPalette({ onNavigate, onCreateTask }: CommandPaletteProps) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
const { setTheme, theme } = useTheme();
|
||||
|
||||
useEffect(() => {
|
||||
const down = (e: KeyboardEvent) => {
|
||||
if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
|
||||
e.preventDefault();
|
||||
setOpen((open) => !open);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener("keydown", down);
|
||||
return () => document.removeEventListener("keydown", down);
|
||||
}, []);
|
||||
|
||||
const runCommand = (command: () => void) => {
|
||||
setOpen(false);
|
||||
command();
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogContent className="p-0 overflow-hidden shadow-2xl max-w-2xl">
|
||||
<Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
|
||||
<div className="flex items-center border-b px-3" cmdk-input-wrapper="">
|
||||
<Search className="mr-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
<Command.Input
|
||||
placeholder={t('search.placeholder') || "Type a command or search..."}
|
||||
className="flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50"
|
||||
/>
|
||||
</div>
|
||||
<Command.List className="max-h-[300px] overflow-y-auto overflow-x-hidden p-2">
|
||||
<Command.Empty>{t('search.noResults') || "No results found."}</Command.Empty>
|
||||
|
||||
<Command.Group heading="Actions">
|
||||
<Command.Item
|
||||
onSelect={() => runCommand(onCreateTask)}
|
||||
className="relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-accent aria-selected:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 cursor-pointer"
|
||||
>
|
||||
<Plus className="mr-2 h-4 w-4" />
|
||||
<span>{t('taskCreation.title') || "Create New Task"}</span>
|
||||
<span className="ml-auto text-xs text-muted-foreground">Cmd+N</span>
|
||||
</Command.Item>
|
||||
|
||||
<Command.Item
|
||||
onSelect={() => runCommand(() => setTheme(theme === 'dark' ? 'light' : 'dark'))}
|
||||
className="relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-accent aria-selected:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 cursor-pointer"
|
||||
>
|
||||
{theme === 'dark' ? <Sun className="mr-2 h-4 w-4" /> : <Moon className="mr-2 h-4 w-4" />}
|
||||
<span>{t('theme.toggle') || "Toggle Theme"}</span>
|
||||
</Command.Item>
|
||||
</Command.Group>
|
||||
|
||||
<Command.Group heading="Navigation">
|
||||
<Command.Item
|
||||
onSelect={() => runCommand(() => onNavigate('tasks'))}
|
||||
className="relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-accent aria-selected:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 cursor-pointer"
|
||||
>
|
||||
<CheckSquare className="mr-2 h-4 w-4" />
|
||||
<span>Tasks</span>
|
||||
</Command.Item>
|
||||
|
||||
<Command.Item
|
||||
onSelect={() => runCommand(() => onNavigate('calendar'))}
|
||||
className="relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-accent aria-selected:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 cursor-pointer"
|
||||
>
|
||||
<Calendar className="mr-2 h-4 w-4" />
|
||||
<span>Calendar</span>
|
||||
</Command.Item>
|
||||
|
||||
<Command.Item
|
||||
onSelect={() => runCommand(() => onNavigate('weeklist'))}
|
||||
className="relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-accent aria-selected:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 cursor-pointer"
|
||||
>
|
||||
<List className="mr-2 h-4 w-4" />
|
||||
<span>Week List</span>
|
||||
</Command.Item>
|
||||
|
||||
<Command.Item
|
||||
onSelect={() => runCommand(() => onNavigate('kanban'))}
|
||||
className="relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-accent aria-selected:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 cursor-pointer"
|
||||
>
|
||||
<LayoutGrid className="mr-2 h-4 w-4" />
|
||||
<span>Kanban Board</span>
|
||||
</Command.Item>
|
||||
|
||||
<Command.Item
|
||||
onSelect={() => runCommand(() => onNavigate('settings'))}
|
||||
className="relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-accent aria-selected:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 cursor-pointer"
|
||||
>
|
||||
<Settings className="mr-2 h-4 w-4" />
|
||||
<span>Settings</span>
|
||||
</Command.Item>
|
||||
</Command.Group>
|
||||
</Command.List>
|
||||
</Command>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user