PreferencesUser
Game Verse

Units CRUD Pattern

Convention notes when implementing Units with Convex + Next, safe MDX for AI

Objectives

  • Standardize CRUD approach for units (Units) from backend to frontend.
  • Quickly reusable for similar catalog modules (name, abbr, order, active).
  • Follow safe MDX rules as in fumadocs-ai-notes.mdx.

Units Data Model

  • Main fields: name, abbr?, order, active.
  • Should have indexes: by_name (unique check in code), by_active.
  • Every table has order: number and active: boolean for sorting/hiding.
// packages/backend/convex/schema.ts (excerpt)
// units: defineTable({ name: v.string(), abbr: v.optional(v.string()), order: v.number(), active: v.boolean() })
//   .index("by_name", ["name"]).index("by_active", ["active"])

Backend (Convex) patterns

  • File: packages/backend/convex/units.ts.
  • Required standard functions:
    • listBrief: return brief list, sorted by order.
    • create: create new unit, check duplicate name via by_name.
    • update: update name?, abbr?, order?, active?.
    • toggleActive: quick enable/disable active.
    • safeDelete: safe delete, block if products still reference via unitId.
    • reorder: bulk update order to save drag-drop results.
// Reorder sample
export const reorder = mutation({
  args: { items: v.array(v.object({ id: v.id("units"), order: v.number() })) },
  handler: async (ctx, args) => {
    for (const it of args.items) await ctx.db.patch(it.id, { order: it.order });
    return { success: true };
  },
});

Frontend patterns (Next + Convex React)

  • List page: apps/web/src/app/(dashboard)/units/page.tsx.
    • Data: useQuery(api.units.listBrief, {}).
    • Actions: useMutation(api.units.toggleActive), useMutation(api.units.safeDelete), useMutation(api.units.reorder).
    • UI features:
      • Search by name/abbr (client-side).
      • Filter status: All, Active (active=true), Inactive (active=false).
      • Sort by name: click "Name" column header to asc/desc.
      • Sort by order: click "Order" column header to return to order mode.
      • Drag-drop to change order and save via api.units.reorder.
        • Only enable drag-drop when: no search keyword, viewing "Order", filter = All.
      • Quick enable/disable status on list via toggleActive.
// Call Convex in FE
import { useQuery, useMutation } from "convex/react";
import { api } from "@dohy/backend/convex/_generated/api";

const units = useQuery(api.units.listBrief, {});
const toggleActive = useMutation(api.units.toggleActive);
const safeDelete = useMutation(api.units.safeDelete);
const reorder = useMutation(api.units.reorder);

// Example enable/disable
await toggleActive({ id: unitId as any, active: true });

// Example save drag-drop
await reorder({ items: localList.map((u, idx) => ({ id: u._id as any, order: idx })) });
  • Navigation (Sidebar + TopNav) needs updating when adding new modules to avoid forgetting:
// apps/web/src/app/(dashboard)/layout.tsx
// Add link to `links` array
const links = [
  { title: "Overview", href: "/dashboard" },
  { title: "Products", href: "/products" }, // example new module
  { title: "Units", href: "/units" },
];
// apps/web/src/components/layout/data/sidebar-data.ts
// Add item to appropriate group (e.g. General group)
{ title: 'Products', url: '/products', icon: Package }
  • Create new page: apps/web/src/app/(dashboard)/units/new/page.tsx.
    • Form: name (required), abbr?, order.
    • Call useMutation(api.units.create) and redirect to /units on success.
await create({ name, abbr: abbr || undefined, order });
  • Edit page: apps/web/src/app/(dashboard)/units/[id]/edit/unit-edit.client.tsx.
    • Load from listBrief to get record by _id.
    • Update via useMutation(api.units.update) with name?, abbr?, order?.
await update({ id: _id as any, name, abbr: abbr || undefined, order });

Quick UX Conventions

  • Display status: "Active" when active=true, "Inactive" when active=false.
  • Quick button: "Activate" when inactive, "Deactivate" when active.
  • Delete: use safeDelete; if products still reference it, show clear error message.

Checklist for Creating Similar Catalog CRUD

  1. Schema: add table with name, order, active (and abbr? if needed) + indexes by_name, by_active.
  2. Backend module: add listBrief, create, update, toggleActive, safeDelete, reorder.
  3. FE pages:
    • List: table with search, status filter, sort by name, drag-drop by order, quick enable/disable.
    • New/Edit: simple form with Card + Input + Label + Button.
  4. Connect Convex: useQuery/useMutation use @dohy/backend/convex/_generated/api.
  5. Quick test: run dev for Convex to generate _generated and check /units page.
  6. Update navigation:
    • TopNav: edit apps/web/src/app/(dashboard)/layout.tsx to add link to links.
    • Sidebar: edit apps/web/src/components/layout/data/sidebar-data.ts to add item to appropriate group.

Safe MDX Notes (per fumadocs-ai-notes.mdx)

  • Wrap all special characters in inline code with backticks: e.g. [{ id, order }], { name?, abbr? }.
  • Put examples in 3-backtick code blocks to avoid parse errors.
  • Don't embed strange JSX in MDX; only Markdown content + code blocks.