Files
Ghost_Kitchen/src/routes/admin/menu/$menuItemId.tsx
T
lukasandClaude Sonnet 5 c8d1721358 Add an admin panel: orders dashboard, menu management, kitchen status
Introduces a role-gated /admin section (sidebar layout, requireAdmin
checked both in route loaders and independently in every admin server
function) with three sections:
- Orders: every order across all customers, and an admin-only order
  detail view nested under /admin so the sidebar persists and admin
  access bypasses the guest-token check.
- Menu: create/edit/delete menu items, their options, and their
  preparation steps; deleting an item with past orders is refused
  (protected by the orderItems FK) instead of crashing.
- Kitchen: read-only live view of every machine's busy/idle status and
  per-tag queue depth, with a manual refresh.

Also:
- Adds a `role` field to the user table (Better Auth additionalFields,
  input:false so it can't be self-assigned) and a db:seed-admin script
  to create/promote an admin account.
- Fixes a real bug found while building this: src/server/auth.ts's
  shared session helpers were plain exported functions using a
  server-only import, which the framework couldn't tree-shake out of
  the client bundle once shared across multiple server/*.ts modules —
  this silently broke hydration app-wide. Fixed with createServerOnlyFn.
- Removes the now-fully-unused @tanstack/devtools-vite plugin.
- Disables view transitions for in-admin navigation so the persistent
  sidebar doesn't cross-fade/shift on every click.
- Gives the "your orders" empty state the same visual treatment as the
  basket page's.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-10 23:14:28 +02:00

38 lines
1.2 KiB
TypeScript

import { createFileRoute, notFound, useNavigate } from '@tanstack/react-router'
import { MenuItemForm } from '#/components/admin/MenuItemForm.tsx'
import { withAdminGuard } from '#/lib/admin-guard.ts'
import { getAdminMenu, getMachineTagOptions, updateMenuItem } from '#/server/menu-admin.ts'
const EditMenuItemRoute = () => {
const { item, machineTags } = Route.useLoaderData()
const navigate = useNavigate()
return (
<section>
<h1>Edit {item.name}</h1>
<MenuItemForm
machineTags={machineTags}
initialItem={item}
submitLabel="Save changes"
onSubmit={async (input) => {
await updateMenuItem({ data: { menuItemId: item.id, ...input } })
await navigate({ to: '/admin/menu', viewTransition: false })
}}
/>
</section>
)
}
export const Route = createFileRoute('/admin/menu/$menuItemId')({
loader: async ({ params }) => {
const [items, machineTags] = await withAdminGuard(() =>
Promise.all([getAdminMenu(), getMachineTagOptions()]),
)
const item = items.find((i) => i.id === Number(params.menuItemId))
if (!item) throw notFound()
return { item, machineTags }
},
component: EditMenuItemRoute,
})