- An unofficial side activity. (Definitely not Flappy Bird.) Earn 1 XP per 15 obstacles
+ An unofficial side activity. Earn 1 XP per 15 obstacles
passed in a single run.
diff --git a/src/app/(frontend)/layout.tsx b/src/app/(frontend)/layout.tsx
index 3edb259..5bf69a7 100644
--- a/src/app/(frontend)/layout.tsx
+++ b/src/app/(frontend)/layout.tsx
@@ -14,6 +14,9 @@ import { ShipmentToasts } from "@/components/frontend/logistics/ShipmentToasts";
import { GameTickRealtime } from "@/components/frontend/realtime/GameTickRealtime";
import { LandingPage } from "@/components/frontend/LandingPage";
import { Toaster } from "@/components/ui/sonner";
+import { CommandPalette } from "@/components/command-palette/CommandPalette";
+import { CommandPaletteProvider } from "@/components/command-palette/CommandPaletteContext";
+import { KeyboardShortcutsProvider } from "@/components/command-palette/KeyboardShortcutsProvider";
// Skip static prerendering of this layout (and all pages under (frontend)/).
// The layout calls `getPayload()` at render time to resolve the current user,
@@ -93,22 +96,36 @@ export default async function RootLayout(props: { children: React.ReactNode }) {
{user ? (
-
-
-
- {children}
-
+ >
+
+
+
+
+ {children}
+
+
+
+
) : (
diff --git a/src/components/frontend/SiteHeader.tsx b/src/components/frontend/SiteHeader.tsx
index 5a9555f..469552a 100644
--- a/src/components/frontend/SiteHeader.tsx
+++ b/src/components/frontend/SiteHeader.tsx
@@ -1,12 +1,17 @@
import { SidebarTrigger } from "@/components/ui/sidebar";
import { Separator } from "@/components/ui/separator";
import { NotificationsBell } from "@/components/frontend/notifications/NotificationsBell";
+import { Command } from "lucide-react";
export const SiteHeader = () => {
return (
+
+
+ K
+
diff --git a/src/components/frontend/banking/BankingOverview.tsx b/src/components/frontend/banking/BankingOverview.tsx
index 65cb9a2..fc415b3 100644
--- a/src/components/frontend/banking/BankingOverview.tsx
+++ b/src/components/frontend/banking/BankingOverview.tsx
@@ -1,14 +1,15 @@
"use client";
-import type { ReactNode } from "react";
+import { useState, type ReactNode } from "react";
import type { BankAccount, BankTransaction, Resource } from "@/payload-types";
-import { LandmarkIcon } from "lucide-react";
+import { LandmarkIcon, PlusIcon } from "lucide-react";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { AccountCard } from "@/components/frontend/banking/AccountCard";
import { CreateAccountDialog } from "@/components/frontend/banking/CreateAccountDialog";
import { WalletHero } from "@/components/frontend/banking/WalletHero";
import { ActivityFeed } from "@/components/frontend/banking/ActivityFeed";
import { currencyLabel, formatAmount } from "@/lib/banking/format";
+import { usePageCommands } from "@/components/command-palette/CommandPaletteContext";
interface BankingOverviewProps {
accounts: BankAccount[];
@@ -55,6 +56,19 @@ export function BankingOverview({
isManager,
factions,
}: BankingOverviewProps) {
+ const [createAccountOpen, setCreateAccountOpen] = useState(false);
+
+ usePageCommands([
+ {
+ id: "banking:create-account",
+ group: "Banking",
+ label: "Create Bank Account",
+ icon: PlusIcon,
+ keywords: ["new", "account", "bank"],
+ onSelect: () => setCreateAccountOpen(true),
+ },
+ ]);
+
const treasury = accounts.filter((a) => a.accountType === "treasury");
const factionAccounts = accounts.filter((a) => a.accountType === "faction");
const personal = accounts.filter((a) => a.accountType === "personal");
@@ -129,7 +143,12 @@ export function BankingOverview({
{visibleAccountsCount}
-
+
{visibleAccountsCount === 0 ? (
diff --git a/src/components/frontend/helpdesk/TicketsView.tsx b/src/components/frontend/helpdesk/TicketsView.tsx
index 19aae39..9572937 100644
--- a/src/components/frontend/helpdesk/TicketsView.tsx
+++ b/src/components/frontend/helpdesk/TicketsView.tsx
@@ -10,8 +10,9 @@ import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { CreateTicketDialog } from "@/components/frontend/helpdesk/CreateTicketDialog";
import { TicketCard } from "@/components/frontend/helpdesk/TicketCard";
import { CLOSING_STATUSES, STATUS_STYLES, type TicketStatus } from "@/lib/tickets/ticketMeta";
-import { InboxIcon, SearchIcon } from "lucide-react";
+import { InboxIcon, PlusIcon, SearchIcon } from "lucide-react";
import { cn } from "@/lib/utils";
+import { usePageCommands } from "@/components/command-palette/CommandPaletteContext";
type TicketFilter = "all" | "open" | "resolved" | "closed" | "unassigned";
@@ -56,6 +57,18 @@ function isUnassignedTicket(ticket: Ticket): boolean {
export function TicketsView({ tickets, currentUserId: _currentUserId, isStaff }: TicketsViewProps) {
const [activeFilter, setActiveFilter] = useState("all");
const [search, setSearch] = useState("");
+ const [createOpen, setCreateOpen] = useState(false);
+
+ usePageCommands([
+ {
+ id: "helpdesk:create-ticket",
+ group: "Helpdesk",
+ label: "Create Support Ticket",
+ icon: PlusIcon,
+ keywords: ["help", "support", "new", "ticket"],
+ onSelect: () => setCreateOpen(true),
+ },
+ ]);
const counts = useMemo>(
() => ({
@@ -132,7 +145,7 @@ export function TicketsView({ tickets, currentUserId: _currentUserId, isStaff }:
: "Keep an eye on your open requests."}
-
+