pelagia-portal/App/components/po/terms-field.tsx
Hardik a99b2ed5df
All checks were successful
PR checks / checks (pull_request) Successful in 43s
PR checks / integration (pull_request) Successful in 31s
feat(po): admin-managed Terms & Conditions catalogue + PO dropdowns (#11)
Mirrors the Place-of-Delivery (#19) pattern: an admin clause library that feeds
the PO T&C fields as dropdowns. (No "work order" type — POs only, per steer.)

- schema + migration: TermsCondition (category enum + text + isActive); the
  migration seeds the prior TC_DEFAULTS as the starting clauses.
- permission manage_terms (Manager + SuperUser + Admin).
- admin screen /admin/terms: table + Add/Edit dialogs + activate/deactivate +
  delete (mirrors /admin/delivery-locations); sidebar link under Administration.
- PO forms (new / edit / manager-edit): the five named T&C slots (Delivery /
  Dispatch / Inspection / Transit Insurance / Payment Terms) become a shared
  <TermsField> select sourced from active clauses of that category; "Others"
  stays free text; the fixed boilerplate lines are untouched.
- tc* columns stay free-text SNAPSHOTS (export/import unchanged); a current value
  not among active clauses is preserved as a "(current)" option.
- tests: terms CRUD + permission guard + grouping helper (6).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 03:38:32 +05:30

38 lines
1.1 KiB
TypeScript

"use client";
/**
* A single PO Terms & Conditions slot (issue #11) — a native
* <select name={field}> sourced from the admin-managed clauses for its category.
* Plain HTML so it works with the forms' native FormData submission.
*
* `options` are the active clause texts (also the stored value). `current` is the
* PO's existing/default value for this slot; if it isn't one of the active
* options (a removed clause, or an older custom value) it is preserved as a
* leading "(current)" option so an edit never silently drops it.
*/
export function TermsField({
field,
options,
current,
className,
}: {
field: string;
options: string[];
current?: string | null;
className?: string;
}) {
const cur = (current ?? "").trim();
const currentMissing = cur.length > 0 && !options.includes(cur);
return (
<select name={field} defaultValue={cur} className={className}>
<option value=""></option>
{currentMissing && <option value={cur}>{cur} (current)</option>}
{options.map((o) => (
<option key={o} value={o}>
{o}
</option>
))}
</select>
);
}