chore(style): codebase em-dash sweep + minor layout polish
Replaces every em-dash and en-dash with regular ASCII hyphens across comments, JSX strings, and dev-facing logs. Mostly cosmetic but stops the inconsistent mix that crept in over the last few months (some files used em-dashes in comments, others didn't, some used both). Bundles two small dashboard-layout tweaks that touch a couple of already-modified files: - (dashboard)/layout.tsx main padding goes from p-6 to pt-3 px-6 pb-6 so page content sits closer to the topbar. - Sidebar now receives the ports list it needs for the footer port switcher. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -70,7 +70,7 @@ export function getClientColumns({
|
||||
enableSorting: false,
|
||||
cell: ({ row }) => {
|
||||
const primary = row.original.contacts?.find((c) => c.isPrimary);
|
||||
if (!primary) return <span className="text-muted-foreground">—</span>;
|
||||
if (!primary) return <span className="text-muted-foreground">-</span>;
|
||||
return (
|
||||
<span className="text-sm">
|
||||
<span className="text-muted-foreground capitalize">{primary.channel}: </span>
|
||||
@@ -86,7 +86,7 @@ export function getClientColumns({
|
||||
cell: ({ getValue }) => {
|
||||
const iso = getValue() as string | null;
|
||||
return (
|
||||
<span className="text-muted-foreground">{iso ? getCountryName(iso, 'en') : '—'}</span>
|
||||
<span className="text-muted-foreground">{iso ? getCountryName(iso, 'en') : '-'}</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
@@ -96,7 +96,7 @@ export function getClientColumns({
|
||||
header: 'Source',
|
||||
cell: ({ getValue }) => {
|
||||
const source = getValue() as string | null;
|
||||
if (!source) return <span className="text-muted-foreground">—</span>;
|
||||
if (!source) return <span className="text-muted-foreground">-</span>;
|
||||
return (
|
||||
<Badge variant="outline" className="capitalize text-xs">
|
||||
{SOURCE_LABELS[source] ?? source}
|
||||
@@ -111,7 +111,7 @@ export function getClientColumns({
|
||||
cell: ({ row }) => {
|
||||
const c = row.original.yachtCount ?? 0;
|
||||
return c === 0 ? (
|
||||
<span className="text-muted-foreground">—</span>
|
||||
<span className="text-muted-foreground">-</span>
|
||||
) : (
|
||||
<Badge variant="secondary" className="text-xs">
|
||||
{c}
|
||||
@@ -126,7 +126,7 @@ export function getClientColumns({
|
||||
cell: ({ row }) => {
|
||||
const c = row.original.companyCount ?? 0;
|
||||
return c === 0 ? (
|
||||
<span className="text-muted-foreground">—</span>
|
||||
<span className="text-muted-foreground">-</span>
|
||||
) : (
|
||||
<Badge variant="secondary" className="text-xs">
|
||||
{c}
|
||||
@@ -140,7 +140,7 @@ export function getClientColumns({
|
||||
enableSorting: false,
|
||||
cell: ({ row }) => {
|
||||
const clientTags = row.original.tags ?? [];
|
||||
if (clientTags.length === 0) return <span className="text-muted-foreground">—</span>;
|
||||
if (clientTags.length === 0) return <span className="text-muted-foreground">-</span>;
|
||||
return (
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{clientTags.slice(0, 3).map((tag) => (
|
||||
|
||||
@@ -33,7 +33,7 @@ interface ClientCompaniesTabProps {
|
||||
|
||||
function formatSince(startDate: string | Date): string {
|
||||
const d = typeof startDate === 'string' ? new Date(startDate) : startDate;
|
||||
if (Number.isNaN(d.getTime())) return '—';
|
||||
if (Number.isNaN(d.getTime())) return '-';
|
||||
return format(d, 'MMM d, yyyy');
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ export function ClientCompaniesTab({ clientId: _clientId, companies }: ClientCom
|
||||
Primary
|
||||
</Badge>
|
||||
) : (
|
||||
<span className="text-muted-foreground">—</span>
|
||||
<span className="text-muted-foreground">-</span>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className="text-muted-foreground text-sm">
|
||||
|
||||
@@ -169,7 +169,7 @@ export function ClientDetailHeader({ client }: ClientDetailHeaderProps) {
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Top-right: archive/restore as a small icon button — destructive
|
||||
{/* Top-right: archive/restore as a small icon button - destructive
|
||||
action sits out of the primary action flow. */}
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@@ -150,7 +150,7 @@ export function ClientForm({ open, onOpenChange, client, onUseExistingClient }:
|
||||
</SheetHeader>
|
||||
|
||||
<form onSubmit={handleSubmit((data) => mutation.mutate(data))} className="space-y-6 py-6">
|
||||
{/* Dedup suggestion — only on the create path. Watches the
|
||||
{/* Dedup suggestion - only on the create path. Watches the
|
||||
live form values for email / phone / name and surfaces
|
||||
an existing client when one matches. The user can
|
||||
attach the new interest to that client instead of
|
||||
|
||||
@@ -180,7 +180,7 @@ function InterestPreviewDrawer({
|
||||
}) {
|
||||
// Pin the most recently selected interest so the drawer stays populated
|
||||
// during the close-animation tail (Vaul keeps the content mounted ~250ms
|
||||
// after `open=false`). Conditional setState is safe here — the guard
|
||||
// after `open=false`). Conditional setState is safe here - the guard
|
||||
// ensures it only fires when the prop actually changes to a new row.
|
||||
const [pinned, setPinned] = useState<ClientInterestRow | null>(interest);
|
||||
if (interest && interest !== pinned) setPinned(interest);
|
||||
@@ -243,7 +243,7 @@ function InterestPreviewDrawer({
|
||||
</DrawerHeader>
|
||||
|
||||
<div className="space-y-5 overflow-y-auto px-4 pb-4">
|
||||
{/* Pipeline-stepper segmented bar — the same primitive used on the
|
||||
{/* Pipeline-stepper segmented bar - the same primitive used on the
|
||||
row card, so the at-a-glance progress hint is consistent
|
||||
across surfaces. */}
|
||||
{stage ? (
|
||||
@@ -255,7 +255,7 @@ function InterestPreviewDrawer({
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{/* Milestones — three sections matching the full interest detail
|
||||
{/* Milestones - three sections matching the full interest detail
|
||||
page (EOI / Deposit / Contract). Done-state is derived from
|
||||
the pipeline stage so seed data without per-step dates still
|
||||
renders correctly. The full milestone columns + per-step
|
||||
@@ -308,7 +308,7 @@ function InterestPreviewDrawer({
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Compact key/value pairs — lead category, source, last contact,
|
||||
{/* Compact key/value pairs - lead category, source, last contact,
|
||||
activity. Each row collapses cleanly when its value is
|
||||
missing so the drawer scales from sparse seed data to full
|
||||
records without empty placeholders. */}
|
||||
|
||||
@@ -106,8 +106,8 @@ function lastActivityLabel(interests: ClientInterestRow[]): string | null {
|
||||
interface PipelineSummaryProps {
|
||||
clientId: string;
|
||||
/**
|
||||
* `hero` — single-line pulse for the detail header (highest active stage only).
|
||||
* `panel` — compact list of every active interest, for the Overview tab.
|
||||
* `hero` - single-line pulse for the detail header (highest active stage only).
|
||||
* `panel` - compact list of every active interest, for the Overview tab.
|
||||
*/
|
||||
variant?: 'hero' | 'panel';
|
||||
}
|
||||
|
||||
@@ -74,9 +74,9 @@ export function ClientYachtsTab({ clientId: _clientId, yachts }: ClientYachtsTab
|
||||
</Link>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{y.lengthFt && y.widthFt ? `${y.lengthFt} × ${y.widthFt} ft` : '—'}
|
||||
{y.lengthFt && y.widthFt ? `${y.lengthFt} × ${y.widthFt} ft` : '-'}
|
||||
</TableCell>
|
||||
<TableCell>{y.hullNumber ?? '—'}</TableCell>
|
||||
<TableCell>{y.hullNumber ?? '-'}</TableCell>
|
||||
<TableCell className="capitalize">{y.status.replace('_', ' ')}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
|
||||
@@ -225,10 +225,10 @@ function ContactRow({
|
||||
|
||||
{/* Bottom / right: tag + actions.
|
||||
Two layers of hiding compose here:
|
||||
(a) phoneEditing — when the phone editor is open, hide the entire
|
||||
(a) phoneEditing - when the phone editor is open, hide the entire
|
||||
action cluster (tag + star + trash) so the user can focus on
|
||||
the form without chips fighting for space.
|
||||
(b) contact.value — when the value is empty (stale import row,
|
||||
(b) contact.value - when the value is empty (stale import row,
|
||||
aborted edit), hide just the tag + Make-primary star;
|
||||
neither makes sense without a value. The trash icon stays
|
||||
so the user can clean up the empty entry.
|
||||
|
||||
@@ -63,7 +63,7 @@ export function DedupSuggestionPanel({
|
||||
useEffect(() => {
|
||||
const t = setTimeout(() => {
|
||||
setDebounced({ email: email ?? '', phone: phone ?? '', name: name ?? '' });
|
||||
// Clear the dismissed flag when inputs change — the user typed
|
||||
// Clear the dismissed flag when inputs change - the user typed
|
||||
// something new, so the prior dismissal no longer applies.
|
||||
setDismissed(false);
|
||||
}, 300);
|
||||
@@ -83,7 +83,7 @@ export function DedupSuggestionPanel({
|
||||
return apiFetch<{ data: MatchData[] }>(`/api/v1/clients/match-candidates?${params}`);
|
||||
},
|
||||
enabled: hasSomething && !dismissed,
|
||||
// Same query is fine to cache for a minute — moves are slow at this layer.
|
||||
// Same query is fine to cache for a minute - moves are slow at this layer.
|
||||
staleTime: 60_000,
|
||||
});
|
||||
|
||||
@@ -120,7 +120,7 @@ export function DedupSuggestionPanel({
|
||||
<p className="text-sm font-semibold leading-tight">
|
||||
{isHigh
|
||||
? 'This looks like an existing client'
|
||||
: 'Possible match — check before creating'}
|
||||
: 'Possible match - check before creating'}
|
||||
</p>
|
||||
<div className="mt-2 rounded-md border bg-background/80 p-2.5">
|
||||
<div className="flex items-center gap-2">
|
||||
|
||||
@@ -74,7 +74,7 @@ export function GdprExportButton({ clientId }: { clientId: string }) {
|
||||
},
|
||||
}),
|
||||
onSuccess: () => {
|
||||
toast.success('Export queued — refresh in ~30 seconds');
|
||||
toast.success('Export queued - refresh in ~30 seconds');
|
||||
qc.invalidateQueries({ queryKey });
|
||||
setEmailOverride('');
|
||||
},
|
||||
@@ -128,7 +128,7 @@ export function GdprExportButton({ clientId }: { clientId: string }) {
|
||||
Email the bundle when ready
|
||||
</Label>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Sends a 7-day signed download link to the client's primary email — or to the
|
||||
Sends a 7-day signed download link to the client's primary email - or to the
|
||||
override below.
|
||||
</p>
|
||||
{emailToClient ? (
|
||||
|
||||
Reference in New Issue
Block a user