feat(palette): add account and display quick actions
Add provisioning a personal bank account, toggling background texture and callsign display, and copying the page link; the layout now passes user preferences and personal-account state so the palette can hide or handle them. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
parent
c4a24483f6
commit
6bcd38aff6
3 changed files with 146 additions and 59 deletions
|
|
@ -72,40 +72,50 @@ export default async function RootLayout(props: { children: React.ReactNode }) {
|
|||
let isIntelligence = false;
|
||||
let isLogistics = false;
|
||||
let currentRank = "";
|
||||
let hasPersonalBankAccount = false;
|
||||
|
||||
const shimsGlobal = await payload.findGlobal({ slug: "shims" });
|
||||
const shimEntries = (shimsGlobal.shims ?? []) as NonNullable<Shim["shims"]>;
|
||||
|
||||
if (user) {
|
||||
[isIntelligence, isLogistics] = await Promise.all([
|
||||
hasIntelligenceQualification(payload, user).catch(() => false),
|
||||
hasLogisticsQualification(payload, user).catch(() => false),
|
||||
]);
|
||||
const [profileRes, experienceRes] = await Promise.all([
|
||||
payload.find({
|
||||
collection: "profiles",
|
||||
where: { user: { equals: user.id } },
|
||||
select: {
|
||||
rank: true,
|
||||
progression: {
|
||||
awards: true,
|
||||
scoreboard: true,
|
||||
xp: true,
|
||||
minigames: true,
|
||||
const [intelligenceQual, logisticsQual, profileRes, experienceRes, personalAccountRes] =
|
||||
await Promise.all([
|
||||
hasIntelligenceQualification(payload, user).catch(() => false),
|
||||
hasLogisticsQualification(payload, user).catch(() => false),
|
||||
payload.find({
|
||||
collection: "profiles",
|
||||
where: { user: { equals: user.id } },
|
||||
select: {
|
||||
rank: true,
|
||||
progression: {
|
||||
awards: true,
|
||||
scoreboard: true,
|
||||
xp: true,
|
||||
minigames: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
payload.find({
|
||||
collection: "experience",
|
||||
sort: "requiredPoints",
|
||||
limit: 100,
|
||||
select: {
|
||||
name: true,
|
||||
description: true,
|
||||
requiredPoints: true,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
}),
|
||||
payload.find({
|
||||
collection: "experience",
|
||||
sort: "requiredPoints",
|
||||
limit: 100,
|
||||
select: {
|
||||
name: true,
|
||||
description: true,
|
||||
requiredPoints: true,
|
||||
},
|
||||
}),
|
||||
payload.find({
|
||||
collection: "bank-accounts",
|
||||
where: {
|
||||
and: [{ accountType: { equals: "personal" } }, { ownerUser: { equals: user.id } }],
|
||||
},
|
||||
limit: 1,
|
||||
}),
|
||||
]);
|
||||
isIntelligence = intelligenceQual;
|
||||
isLogistics = logisticsQual;
|
||||
hasPersonalBankAccount = personalAccountRes.docs.length > 0;
|
||||
|
||||
const userXp = profileRes.docs[0]?.progression?.xp ?? 0;
|
||||
xpLevel = resolveLevel(userXp, experienceRes.docs);
|
||||
|
|
@ -155,6 +165,8 @@ export default async function RootLayout(props: { children: React.ReactNode }) {
|
|||
hasIntelligence={isIntelligence}
|
||||
hasLogistics={isLogistics}
|
||||
username={user.username}
|
||||
preferences={user.preferences}
|
||||
hasPersonalBankAccount={hasPersonalBankAccount}
|
||||
/>
|
||||
</CommandPaletteProvider>
|
||||
</KeyboardShortcutsProvider>
|
||||
|
|
|
|||
|
|
@ -24,12 +24,17 @@ import {
|
|||
} from "./CommandPaletteContext";
|
||||
import { markAllNotificationsRead } from "@/app/(frontend)/notifications/actions";
|
||||
import { autoArrangeLocker } from "@/app/(frontend)/locker/actions";
|
||||
import { ensureMyAccount } from "@/app/(frontend)/logistics/banking/actions";
|
||||
import { updatePreferences } from "@/app/(frontend)/account/actions";
|
||||
import type { User as PayloadUser } from "@/payload-types";
|
||||
|
||||
interface CommandPaletteProps {
|
||||
userRoles: string[];
|
||||
hasIntelligence: boolean;
|
||||
hasLogistics: boolean;
|
||||
username?: string;
|
||||
preferences?: NonNullable<PayloadUser["preferences"]>;
|
||||
hasPersonalBankAccount?: boolean;
|
||||
}
|
||||
|
||||
export function CommandPalette({
|
||||
|
|
@ -37,6 +42,8 @@ export function CommandPalette({
|
|||
hasIntelligence,
|
||||
hasLogistics,
|
||||
username,
|
||||
preferences,
|
||||
hasPersonalBankAccount,
|
||||
}: CommandPaletteProps) {
|
||||
const router = useRouter();
|
||||
const [open, setOpen] = React.useState(false);
|
||||
|
|
@ -90,6 +97,64 @@ export function CommandPalette({
|
|||
}
|
||||
break;
|
||||
}
|
||||
case "ensureBankAccount": {
|
||||
const result = await ensureMyAccount();
|
||||
if (result.success) {
|
||||
toast.success("Personal bank account ready");
|
||||
} else {
|
||||
toast.error(result.error ?? "Failed to provision bank account");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "toggleBackgroundTexture": {
|
||||
const result = await updatePreferences({
|
||||
mutedAll: preferences?.notifications?.mutedAll ?? false,
|
||||
mutedTypes: preferences?.notifications?.mutedTypes ?? [],
|
||||
showCallsign: preferences?.display?.showCallsign ?? false,
|
||||
showVersionOverlay: preferences?.display?.showVersionOverlay ?? true,
|
||||
showBackgroundTexture: !(preferences?.display?.showBackgroundTexture ?? true),
|
||||
leadOrActual: preferences?.display?.leadOrActual ?? "lead",
|
||||
discordEnabled: preferences?.discord?.enabled ?? false,
|
||||
discordMutedTypes: preferences?.discord?.mutedTypes ?? [],
|
||||
xpDisplayMini: preferences?.display?.xpDisplayMini ?? false,
|
||||
});
|
||||
if (result.success) {
|
||||
toast.success("Background texture toggled");
|
||||
router.refresh();
|
||||
} else {
|
||||
toast.error(result.error ?? "Failed to update preferences");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "toggleCallsign": {
|
||||
const result = await updatePreferences({
|
||||
mutedAll: preferences?.notifications?.mutedAll ?? false,
|
||||
mutedTypes: preferences?.notifications?.mutedTypes ?? [],
|
||||
showCallsign: !(preferences?.display?.showCallsign ?? false),
|
||||
showVersionOverlay: preferences?.display?.showVersionOverlay ?? true,
|
||||
showBackgroundTexture: preferences?.display?.showBackgroundTexture ?? true,
|
||||
leadOrActual: preferences?.display?.leadOrActual ?? "lead",
|
||||
discordEnabled: preferences?.discord?.enabled ?? false,
|
||||
discordMutedTypes: preferences?.discord?.mutedTypes ?? [],
|
||||
xpDisplayMini: preferences?.display?.xpDisplayMini ?? false,
|
||||
});
|
||||
if (result.success) {
|
||||
toast.success("Callsign display toggled");
|
||||
router.refresh();
|
||||
} else {
|
||||
toast.error(result.error ?? "Failed to update preferences");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "copyPageLink": {
|
||||
try {
|
||||
await navigator.clipboard.writeText(window.location.href);
|
||||
toast.success("Page link copied to clipboard");
|
||||
} catch {
|
||||
toast.error("Failed to copy page link");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "refreshPage": {
|
||||
router.refresh();
|
||||
toast.success("Page refreshed");
|
||||
|
|
@ -105,7 +170,7 @@ export function CommandPalette({
|
|||
setLoading(null);
|
||||
}
|
||||
},
|
||||
[router],
|
||||
[router, preferences],
|
||||
);
|
||||
|
||||
const filteredNavCommands = React.useMemo<NavigationCommand[]>(() => {
|
||||
|
|
@ -154,9 +219,12 @@ export function CommandPalette({
|
|||
if (cmd.requires === "logistics" && !hasLogistics && !userRoles.includes("developer")) {
|
||||
return false;
|
||||
}
|
||||
if (cmd.actionKey === "ensureBankAccount" && hasPersonalBankAccount) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}, [userRoles, hasIntelligence, hasLogistics]);
|
||||
}, [userRoles, hasIntelligence, hasLogistics, hasPersonalBankAccount]);
|
||||
|
||||
const groupedContextual = React.useMemo(() => {
|
||||
const groups = new Map<string, ContextualCommand[]>();
|
||||
|
|
|
|||
|
|
@ -3,10 +3,11 @@ import {
|
|||
Bell,
|
||||
Backpack,
|
||||
LogOut,
|
||||
PlaneTakeoff,
|
||||
User,
|
||||
RefreshCw,
|
||||
Shield,
|
||||
Wallet,
|
||||
Wallpaper,
|
||||
Tag,
|
||||
Link2,
|
||||
} from "lucide-react";
|
||||
|
||||
export interface ActionCommand {
|
||||
|
|
@ -28,39 +29,37 @@ export interface ActionCommand {
|
|||
|
||||
export const actionCommands: ActionCommand[] = [
|
||||
{
|
||||
id: "action:flight-simulator",
|
||||
label: "Flight Simulator",
|
||||
icon: PlaneTakeoff,
|
||||
keywords: ["flappy", "game", "minigame", "fly"],
|
||||
actionType: "navigate",
|
||||
href: "/flappy",
|
||||
id: "action:provision-bank-account",
|
||||
label: "Provision Personal Bank Account",
|
||||
icon: Wallet,
|
||||
keywords: ["banking", "wallet", "money", "create", "open"],
|
||||
actionType: "callback",
|
||||
actionKey: "ensureBankAccount",
|
||||
},
|
||||
{
|
||||
id: "action:view-notifications",
|
||||
label: "View Notifications",
|
||||
icon: Bell,
|
||||
keywords: ["alerts", "messages", "inbox"],
|
||||
actionType: "navigate",
|
||||
href: "/notifications",
|
||||
id: "action:toggle-background-texture",
|
||||
label: "Toggle Background Texture",
|
||||
icon: Wallpaper,
|
||||
keywords: ["display", "appearance", "theme", "texture", "background"],
|
||||
actionType: "callback",
|
||||
actionKey: "toggleBackgroundTexture",
|
||||
},
|
||||
{
|
||||
id: "action:create-ticket",
|
||||
label: "Create Support Ticket",
|
||||
icon: UserPlus,
|
||||
keywords: ["help", "support", "report"],
|
||||
actionType: "navigate",
|
||||
href: "/helpdesk",
|
||||
id: "action:toggle-callsign",
|
||||
label: "Toggle Callsign Display",
|
||||
icon: Tag,
|
||||
keywords: ["display", "callsign", "name", "sidebar"],
|
||||
actionType: "callback",
|
||||
actionKey: "toggleCallsign",
|
||||
},
|
||||
{
|
||||
id: "action:admin-panel",
|
||||
label: "Open Admin Panel",
|
||||
icon: Shield,
|
||||
keywords: ["cms", "manage", "backend"],
|
||||
actionType: "navigate",
|
||||
href: "/admin",
|
||||
roles: ["admin", "developer"],
|
||||
id: "action:copy-page-link",
|
||||
label: "Copy Page Link",
|
||||
icon: Link2,
|
||||
keywords: ["share", "url", "clipboard", "copy", "link"],
|
||||
actionType: "callback",
|
||||
actionKey: "copyPageLink",
|
||||
},
|
||||
|
||||
{
|
||||
id: "action:mark-all-read",
|
||||
label: "Mark All Notifications Read",
|
||||
|
|
@ -93,4 +92,12 @@ export const actionCommands: ActionCommand[] = [
|
|||
actionType: "callback",
|
||||
actionKey: "logout",
|
||||
},
|
||||
{
|
||||
id: "action:create-ticket",
|
||||
label: "Create Support Ticket",
|
||||
icon: UserPlus,
|
||||
keywords: ["help", "support", "report"],
|
||||
actionType: "navigate",
|
||||
href: "/helpdesk",
|
||||
},
|
||||
];
|
||||
|
|
|
|||
Loading…
Reference in a new issue