Skip to content

moooon

Motir

Vibe your whole project. Bring an idea — Motir's three AI layers plan it, track it, and ship it, end to end. You're looking at Motir, built in Motir.

  • Vibe Project
  • Open Source
  • AI Agent
  • AI Loop
1
requests
0
upvotes
145
planned
1,361
shipped

Motir · Work items

MOTIR-283Done

5.3.2 `customFieldsService` (definitions) — CRUD + option rename/reorder/archive/delete-when-unused + the 50/55 caps, admin-gated, routes

Estimate: 30m · Depends on: 5.3.1

The definitions half of the service. Per the 4-layer rule: lib/services/customFieldsService.ts + typed errors (lib/customFields/errors.ts) + DTOs/mappers + HTTP-only routes.

Definition CRUDcreateField(projectId, { label, fieldType, description?, options? }, ctx) (slug-generates the immutable key, unique per project; seeds initial options for select; enforces the 50-field cap with a typed error), renameField, reorderField (fractional index — the board-settings precedent), deleteField (the team-managed mirror: HARD delete, values destroyed via the cascade; the service returns/exposes the value count so the UI confirm can name it — countValuesByField read first). All project-admin-gated — the 6.4 two-tier check (isWorkspaceManager(wsRole) || projectMembership.role === admin), exactly the members-page pattern.

Option management (select fields) — addOption (55-cap), renameOption, reorderOption, and the verified split: archiveOption (any time — hidden from new selection, existing values keep rendering) vs deleteOption (only when unusedcountValuesByOption === 0, else a typed OptionInUseError; the DB Restrict backstops). Unarchive supported (the inverse is free).

ReadslistFields(projectId, ctx) (admin page: definitions + option sets + per-field value counts, ≤50 bounded); a lighter listFieldsForIssueRail shape is 5.3.3's concern. Routes: GET/POST /api/projects/[id]/fields, PATCH/DELETE /api/fields/[id], POST/PATCH/DELETE under /api/fields/[id]/options — parse → one service call → typed-error mapping (403 not-admin / 404 cross-workspace per finding #44 / 409 in-use / 422 caps).

Acceptance criteria

  • Field create/rename/reorder/delete + option add/rename/reorder/archive/unarchive/delete ship with the caps (50/55 → typed errors), the immutable-key rule, and the only-when-unused option delete (in-use → 409; archive offered); field delete cascades values and the API exposes the pre-delete value count.
  • Every mutation is project-admin-gated (6.4 two-tier); non-admins get 403, cross-workspace 404; a viewer/member can READ definitions (the rail needs them) but not mutate.
  • One service method = one transaction; reorder uses fractional indexing (no renumber sweeps); routes are HTTP-only; pnpm test:coverage ≥90% incl. the cap + in-use branches.

Context refs

  • 5.3.1 models/repos; app/(authed)/settings/project/members/page.tsx (the 6.4 admin-gate pattern to reuse) + lib/projects/roles
  • The board-settings fractional reorder precedent (3.6) for option/field reorder
  • The verified mirror rules in the Story 5.3 description (50/55 caps; hard field delete; archive vs delete-when-unused)
  • lib/services/workspacesService.ts / projectsService DTO+mapper conventions