chore(autonomous-session): consolidate uncommitted work from prior session
Bundles the prior autonomous-session output that was sitting unstaged: - Em-dash sweep across src/ + tests/ (en-dash/em-dash to hyphen, ~2280 instances) - country-flag-icons rollout (CountryFlag component, replaces emoji glyphs that never rendered on Windows; lazy-loads the 3x2 SVG index as a single chunk after the per-subpath dynamic-import approach silently failed in webpack) - Admin IA Phase 1+2: 7-domain regroup, 41 to 38 pages, /admin/berths index, redirects (ocr to ai, reports to dashboard, invitations to users), docs/admin-ia-proposal.md - Per-template email tester (registry + endpoint + UI on Email admin page) - Cancel-document mode picker (delete-from-Documenso vs keep-for-audit) - Dashboard PDF report: 25 widgets, SVG charts, date-range picker, 11 resolvers - Customize-widgets per-region sortables at xl+ (charts/rails/feed); single flat sortable below xl when the layout stacks; per-viewport saved orders - Audit doc updates capturing each shipped item - Lint fixes: react-compiler immutability in DonutChart (reduce instead of let-reassign), set-state-in-effect disables in CountryFlag and UploadForSigning preview-bytes effect, unused 'confirm' destructures in interest contract + reservation tabs, unescaped apostrophe in test-template card copy
This commit is contained in:
@@ -29,7 +29,7 @@ interface ActivityItem {
|
||||
label: string | null;
|
||||
userId: string | null;
|
||||
/** Server-resolved actor display name (from user_profiles). When null,
|
||||
* the actor row no longer exists — render falls back to a "Unknown
|
||||
* the actor row no longer exists - render falls back to a "Unknown
|
||||
* user" sentinel rather than the raw UUID prefix. */
|
||||
actorName: string | null;
|
||||
fieldChanged: string | null;
|
||||
@@ -52,6 +52,55 @@ function humanizeFieldName(name: string): string {
|
||||
.replace(/\b\w/g, (c) => c.toUpperCase());
|
||||
}
|
||||
|
||||
/** Entity type alias map for the feed labels. Most types humanize fine
|
||||
* via `humanizeFieldName`, but a few read awkwardly ("Residential
|
||||
* Client" is clearer than the raw enum, notes flatten to their parent). */
|
||||
const ENTITY_TYPE_LABELS: Record<string, string> = {
|
||||
residential_client: 'Residential client',
|
||||
residential_interest: 'Residential interest',
|
||||
berth_reservation: 'Berth reservation',
|
||||
berth_maintenance_log: 'Berth maintenance',
|
||||
berth_recommendation: 'Berth recommendation',
|
||||
client_note: 'Client note',
|
||||
yacht_note: 'Yacht note',
|
||||
company_note: 'Company note',
|
||||
interest_note: 'Interest note',
|
||||
interest_qualification: 'Interest qualification',
|
||||
document_send: 'Document send',
|
||||
document_folder: 'Document folder',
|
||||
document_template: 'Document template',
|
||||
documentTemplate: 'Document template',
|
||||
form_template: 'Form template',
|
||||
report_template: 'Report template',
|
||||
email_account: 'Email account',
|
||||
email_message: 'Email message',
|
||||
user_email_change: 'Email change',
|
||||
custom_field_definition: 'Custom field',
|
||||
custom_field_values: 'Custom field',
|
||||
expense_export: 'Expense export',
|
||||
gdpr_export: 'GDPR export',
|
||||
qualification_criterion: 'Qualification criterion',
|
||||
website_submission: 'Website submission',
|
||||
webhook_inbound: 'Inbound webhook',
|
||||
webhook_delivery: 'Webhook delivery',
|
||||
audit_log: 'Audit log',
|
||||
portal_user: 'Portal user',
|
||||
portal_session: 'Portal session',
|
||||
portal_auth_token: 'Portal token',
|
||||
client_contact: 'Client contact',
|
||||
clientContact: 'Client contact',
|
||||
clientAddress: 'Client address',
|
||||
companyAddress: 'Company address',
|
||||
clientRelationship: 'Client relationship',
|
||||
company_membership: 'Company membership',
|
||||
crm_invite: 'CRM invite',
|
||||
queue_job: 'Queue job',
|
||||
super_admin: 'Super admin',
|
||||
};
|
||||
function humanizeEntityType(type: string): string {
|
||||
return ENTITY_TYPE_LABELS[type] ?? humanizeFieldName(type);
|
||||
}
|
||||
|
||||
/** Map enum-typed field values to their canonical human labels. The audit
|
||||
* log stores raw enum strings (`deposit_10pct`, `lost_other_marina`); the
|
||||
* feed should read like `10% Deposit`, not the wire value. */
|
||||
@@ -85,13 +134,13 @@ function normalizeEnumValue(field: string, value: unknown): unknown {
|
||||
* count; nulls / empty render as em-dash. */
|
||||
function shortValue(value: unknown, fieldContext?: string): string {
|
||||
if (fieldContext) value = normalizeEnumValue(fieldContext, value);
|
||||
if (value === null || value === undefined || value === '') return '—';
|
||||
if (value === null || value === undefined || value === '') return '-';
|
||||
if (typeof value === 'string') return value;
|
||||
if (typeof value === 'number' || typeof value === 'boolean') return String(value);
|
||||
if (Array.isArray(value)) return `${value.length} item${value.length === 1 ? '' : 's'}`;
|
||||
if (typeof value === 'object') {
|
||||
const entries = Object.entries(value as Record<string, unknown>);
|
||||
if (entries.length === 0) return '—';
|
||||
if (entries.length === 0) return '-';
|
||||
return entries
|
||||
.slice(0, 3)
|
||||
.map(
|
||||
@@ -199,7 +248,7 @@ function ActivityFeedInner() {
|
||||
|
||||
// A1: permission_denied rows on the activity feed render as a bare
|
||||
// action badge with no entity name (they target `admin.X` with empty
|
||||
// entityId). They're noise for the rep — keep them in the audit log
|
||||
// entityId). They're noise for the rep - keep them in the audit log
|
||||
// page but hide them from the dashboard feed.
|
||||
const items = (data ?? []).filter((i) => i.action !== 'permission_denied');
|
||||
|
||||
@@ -245,18 +294,23 @@ function ActivityFeedInner() {
|
||||
space between them. */}
|
||||
<span className="text-muted-foreground/60 mx-1.5">·</span>
|
||||
<span className="text-muted-foreground text-xs capitalize">
|
||||
{item.entityType}
|
||||
{humanizeEntityType(item.entityType)}
|
||||
</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<span className="font-medium capitalize">{item.entityType}</span>
|
||||
{item.entityId && (
|
||||
<span className="ml-1 text-muted-foreground font-mono text-xs">
|
||||
{item.entityId.slice(0, 8)}
|
||||
// No resolvable label - either the entity was
|
||||
// deleted or the type isn't in the server-side
|
||||
// resolver yet. Either way we never expose a
|
||||
// UUID fragment: it reads as noise to the rep
|
||||
// and leaks an internal identifier.
|
||||
<span className="font-medium capitalize">
|
||||
{humanizeEntityType(item.entityType)}
|
||||
{item.entityId ? (
|
||||
<span className="ml-1 text-muted-foreground text-xs font-normal">
|
||||
(removed)
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
) : null}
|
||||
</span>
|
||||
)}
|
||||
</p>
|
||||
{diffLine ? (
|
||||
|
||||
Reference in New Issue
Block a user