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: numberandactive: booleanfor 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 byorder.create: create new unit, check duplicate name viaby_name.update: updatename?,abbr?,order?,active?.toggleActive: quick enable/disableactive.safeDelete: safe delete, block ifproductsstill reference viaunitId.reorder: bulk updateorderto 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
ordermode. - Drag-drop to change
orderand save viaapi.units.reorder.- Only enable drag-drop when: no search keyword, viewing "Order", filter =
All.
- Only enable drag-drop when: no search keyword, viewing "Order", filter =
- Quick enable/disable status on list via
toggleActive.
- Search by
- Data:
// 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/unitson success.
- Form:
await create({ name, abbr: abbr || undefined, order });- Edit page:
apps/web/src/app/(dashboard)/units/[id]/edit/unit-edit.client.tsx.- Load from
listBriefto get record by_id. - Update via
useMutation(api.units.update)withname?,abbr?,order?.
- Load from
await update({ id: _id as any, name, abbr: abbr || undefined, order });Quick UX Conventions
- Display status: "Active" when
active=true, "Inactive" whenactive=false. - Quick button: "Activate" when inactive, "Deactivate" when active.
- Delete: use
safeDelete; ifproductsstill reference it, show clear error message.
Checklist for Creating Similar Catalog CRUD
- Schema: add table with
name,order,active(andabbr?if needed) + indexesby_name,by_active. - Backend module: add
listBrief,create,update,toggleActive,safeDelete,reorder. - 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.
- List: table with search, status filter, sort by name, drag-drop by
- Connect Convex:
useQuery/useMutationuse@dohy/backend/convex/_generated/api. - Quick test: run dev for Convex to generate
_generatedand check/unitspage. - Update navigation:
- TopNav: edit
apps/web/src/app/(dashboard)/layout.tsxto add link tolinks. - Sidebar: edit
apps/web/src/components/layout/data/sidebar-data.tsto add item to appropriate group.
- TopNav: edit
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.