ccfb674318
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.
129 lines
7.2 KiB
TypeScript
129 lines
7.2 KiB
TypeScript
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>
|
|
);
|
|
}
|