fix(ui): humanize enum labels, format dates, resolve actor names, loading skeleton
- Documents hub signer status now renders via a label map (`Pending`, `Signed`, `Declined`, …) instead of the raw lowercase enum value. - Invoice detail formats `dueDate` and `paymentDate` as `MMM d, yyyy` via `date-fns` instead of leaking raw `2025-03-14` ISO strings, and swaps the "Payment Method" free-text input for a `Select` of labelled options (`Bank transfer`, `Credit card`, …) so we never store `bank_transfer` from a hand-typed field again. - Interest tabs `MilestoneSection` status badge uses a `humanizeStatus` helper so values like `waiting_for_signatures` show as `Waiting For Signatures` (correctly title-cased) instead of being a lower-snake-case fragment inside an ALL-CAPS pill. - `OUTCOME_BADGE` in the interest header now has a fall-through that renders any unknown outcome as a closed-state badge, preventing a closed interest from looking open just because its enum was added upstream without a matching label entry. - Interest timeline route joins the `user` table and returns `userName` alongside `userId`; the client renders the resolved name instead of a 36-char UUID. Falls back to `'a teammate'` if the user row was deleted. - Invoice "New / Step 3 — Review" replaces the truncated UUID display with a server-resolved client/company name via a small `useQuery`, so users can confirm they picked the right billing entity before submitting. - New `loading.tsx` for client detail renders a header / tab strip / card skeleton during the server-component / initial-query window that previously flashed empty. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -26,6 +26,12 @@ const LEAD_CATEGORY_OPTIONS = LEAD_CATEGORIES.map((c) => ({
|
||||
label: c.replace(/_/g, ' ').replace(/\b\w/g, (m) => m.toUpperCase()),
|
||||
}));
|
||||
|
||||
// Convert raw enum values like `waiting_for_signatures` → `Waiting For Signatures`.
|
||||
function humanizeStatus(value: string | null): string | null {
|
||||
if (!value) return null;
|
||||
return value.replace(/_/g, ' ').replace(/\b\w/g, (m) => m.toUpperCase());
|
||||
}
|
||||
|
||||
interface InterestTabsOptions {
|
||||
interestId: string;
|
||||
currentUserId?: string;
|
||||
@@ -203,7 +209,7 @@ function MilestoneSection({
|
||||
</div>
|
||||
{status ? (
|
||||
<span className="rounded-full bg-muted px-2 py-0.5 text-[10px] font-medium uppercase tracking-wide text-muted-foreground">
|
||||
{status.replace(/_/g, ' ')}
|
||||
{humanizeStatus(status)}
|
||||
</span>
|
||||
) : null}
|
||||
</header>
|
||||
|
||||
Reference in New Issue
Block a user