Bump typography ~1.5× across the app
Producers were reading the UI at a squint. Two pieces:
1. @theme in globals.css now overrides every --text-* token (xs
through 7xl) to 1.5× the Tailwind v4 default, with matching
line-heights. This covers everything using the named classes
(text-xs / text-sm / text-base / …) without touching markup.
Also bumped .label-upper from 10px → 15px.
2. Scripted sweep of every arbitrary text-[Npx], fontSize={N}, and
inline `fontSize: N` (recharts ticks) — 421 occurrences across
82 files. Each N was rounded to round(N × 1.5). Idempotency is
NOT preserved; running the sweep twice would scale twice.
Script lives at /tmp/scale-text-sizes.mjs for reference — not
committed. If we need to roll back, `git revert` this commit is
safe (pure display change, no schema/service impact).
This commit is contained in:
parent
f2cdd8bd4c
commit
ffd91cea04
82 changed files with 450 additions and 421 deletions
|
|
@ -177,7 +177,7 @@ export default function BriefsPage() {
|
|||
</Button>
|
||||
)}
|
||||
|
||||
<div className="ml-auto text-[11px] text-[var(--muted-foreground)]">
|
||||
<div className="ml-auto text-[17px] text-[var(--muted-foreground)]">
|
||||
{filtered.length} of {rows.length} brief{rows.length === 1 ? "" : "s"}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -252,14 +252,14 @@ function BriefRowView({
|
|||
return (
|
||||
<tr className="border-b transition-colors hover:bg-[var(--muted)]/40">
|
||||
<td className="px-3 py-2">
|
||||
<span className="font-mono text-[10px] text-[var(--muted-foreground)]">
|
||||
<span className="font-mono text-[15px] text-[var(--muted-foreground)]">
|
||||
{brief.source}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-3 py-2">
|
||||
<div className="font-semibold">{brief.title}</div>
|
||||
{brief.description && (
|
||||
<div className="mt-0.5 line-clamp-1 text-[10px] text-[var(--muted-foreground)]">
|
||||
<div className="mt-0.5 line-clamp-1 text-[15px] text-[var(--muted-foreground)]">
|
||||
{brief.description}
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -269,7 +269,7 @@ function BriefRowView({
|
|||
<div>
|
||||
<div>{brief.requestorName}</div>
|
||||
{brief.requestorEmail && (
|
||||
<div className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<div className="text-[15px] text-[var(--muted-foreground)]">
|
||||
{brief.requestorEmail}
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -282,7 +282,7 @@ function BriefRowView({
|
|||
</td>
|
||||
<td className="px-3 py-2">
|
||||
{brief.clientTeam ? (
|
||||
<Badge variant="outline" className="text-[10px]">
|
||||
<Badge variant="outline" className="text-[15px]">
|
||||
{brief.clientTeam.name}
|
||||
</Badge>
|
||||
) : (
|
||||
|
|
@ -313,7 +313,7 @@ function BriefRowView({
|
|||
>
|
||||
<SelectTrigger
|
||||
className={cn(
|
||||
"h-7 border-0 text-[10px] font-bold uppercase tracking-wider",
|
||||
"h-7 border-0 text-[15px] font-bold uppercase tracking-wider",
|
||||
STATUS_STYLES[brief.status]
|
||||
)}
|
||||
>
|
||||
|
|
@ -341,7 +341,7 @@ function BriefRowView({
|
|||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="h-6 text-[10px]"
|
||||
className="h-6 text-[15px]"
|
||||
onClick={onPromote}
|
||||
disabled={brief.status === "REJECTED" || brief.status === "ARCHIVED"}
|
||||
>
|
||||
|
|
@ -539,7 +539,7 @@ function PromoteBriefDialog({
|
|||
placeholder="DJ-2026-001"
|
||||
autoFocus
|
||||
/>
|
||||
<p className="mt-1 text-[10px] text-[var(--muted-foreground)]">
|
||||
<p className="mt-1 text-[15px] text-[var(--muted-foreground)]">
|
||||
Must be unique. Used as the short-form handle for the project.
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -571,7 +571,7 @@ function Th({ label, className }: { label: string; className?: string }) {
|
|||
return (
|
||||
<th
|
||||
className={cn(
|
||||
"px-3 py-2 text-left text-[10px] font-bold uppercase tracking-wider text-[var(--muted-foreground)]",
|
||||
"px-3 py-2 text-left text-[15px] font-bold uppercase tracking-wider text-[var(--muted-foreground)]",
|
||||
className
|
||||
)}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ function KpiCard({
|
|||
<p className="text-2xl font-bold tracking-tight">{value}</p>
|
||||
<p className="text-xs text-[var(--muted-foreground)]">{title}</p>
|
||||
{subtitle && (
|
||||
<p className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<p className="text-[15px] text-[var(--muted-foreground)]">
|
||||
{subtitle}
|
||||
</p>
|
||||
)}
|
||||
|
|
@ -237,13 +237,13 @@ export default function DashboardPage() {
|
|||
>
|
||||
<XAxis
|
||||
dataKey="name"
|
||||
tick={{ fontSize: 10 }}
|
||||
tick={{ fontSize: 15 }}
|
||||
interval={0}
|
||||
angle={-45}
|
||||
textAnchor="end"
|
||||
height={60}
|
||||
/>
|
||||
<YAxis tick={{ fontSize: 10 }} allowDecimals={false} />
|
||||
<YAxis tick={{ fontSize: 15 }} allowDecimals={false} />
|
||||
<RTooltip
|
||||
contentStyle={{
|
||||
fontSize: "12px",
|
||||
|
|
@ -310,7 +310,7 @@ export default function DashboardPage() {
|
|||
>
|
||||
{deliv.name}
|
||||
</Link>
|
||||
<p className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<p className="text-[15px] text-[var(--muted-foreground)]">
|
||||
{deliv.project.name}
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -424,7 +424,7 @@ export default function DeliverablesPage() {
|
|||
className="border-b transition-colors hover:bg-[var(--muted)]/40"
|
||||
>
|
||||
<td className="px-3 py-2 align-middle">
|
||||
<span className="font-mono text-[11px] tabular-nums text-[var(--muted-foreground)]">
|
||||
<span className="font-mono text-[17px] tabular-nums text-[var(--muted-foreground)]">
|
||||
{r.project.omgJobNumber ?? "—"}
|
||||
</span>
|
||||
</td>
|
||||
|
|
@ -435,7 +435,7 @@ export default function DeliverablesPage() {
|
|||
>
|
||||
<span className="font-medium">{r.project.name}</span>
|
||||
{r.project.clientTeam && (
|
||||
<span className="ml-1.5 text-[10px] text-[var(--muted-foreground)]">
|
||||
<span className="ml-1.5 text-[15px] text-[var(--muted-foreground)]">
|
||||
· {r.project.clientTeam.name}
|
||||
</span>
|
||||
)}
|
||||
|
|
@ -467,12 +467,12 @@ export default function DeliverablesPage() {
|
|||
{due ? format(due, "MMM d") : <span className="text-[var(--muted-foreground)]">—</span>}
|
||||
</td>
|
||||
<td className="px-3 py-2 align-middle">
|
||||
<Badge variant="outline" className={cn("text-[10px]", PRIORITY_COLORS[r.priority])}>
|
||||
<Badge variant="outline" className={cn("text-[15px]", PRIORITY_COLORS[r.priority])}>
|
||||
{r.priority}
|
||||
</Badge>
|
||||
</td>
|
||||
<td className="px-3 py-2 align-middle">
|
||||
<span className="text-[10px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
<span className="text-[15px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
{r.status.replace(/_/g, " ")}
|
||||
</span>
|
||||
</td>
|
||||
|
|
@ -494,7 +494,7 @@ function Th({ label, className }: { label: string; className?: string }) {
|
|||
return (
|
||||
<th
|
||||
className={cn(
|
||||
"px-3 py-2 text-left text-[10px] font-bold uppercase tracking-wider text-[var(--muted-foreground)]",
|
||||
"px-3 py-2 text-left text-[15px] font-bold uppercase tracking-wider text-[var(--muted-foreground)]",
|
||||
className
|
||||
)}
|
||||
>
|
||||
|
|
@ -522,7 +522,7 @@ function SortableTh({
|
|||
return (
|
||||
<th
|
||||
className={cn(
|
||||
"cursor-pointer select-none px-3 py-2 text-left text-[10px] font-bold uppercase tracking-wider text-[var(--muted-foreground)] hover:text-[var(--foreground)]",
|
||||
"cursor-pointer select-none px-3 py-2 text-left text-[15px] font-bold uppercase tracking-wider text-[var(--muted-foreground)] hover:text-[var(--foreground)]",
|
||||
className
|
||||
)}
|
||||
onClick={() => onClick(sortKey)}
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ export default function NotificationsPage() {
|
|||
<p className="mt-0.5 text-xs text-[var(--muted-foreground)]">
|
||||
{notif.message}
|
||||
</p>
|
||||
<p className="mt-1 text-[10px] text-[var(--muted-foreground)]">
|
||||
<p className="mt-1 text-[15px] text-[var(--muted-foreground)]">
|
||||
{formatDistanceToNow(new Date(notif.createdAt), {
|
||||
addSuffix: true,
|
||||
})}
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ export default function DeliverableDetailPage() {
|
|||
<div className="grid gap-x-6 gap-y-2 rounded-xl border p-4 text-sm shadow-[var(--shadow-xs)] sm:grid-cols-2 md:grid-cols-3">
|
||||
{deliverable.cmfSku && (
|
||||
<div className="sm:col-span-2 md:col-span-3">
|
||||
<p className="text-[10px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
<p className="text-[15px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
CMF / SKU
|
||||
</p>
|
||||
<p className="whitespace-pre-line">{deliverable.cmfSku}</p>
|
||||
|
|
@ -168,7 +168,7 @@ export default function DeliverableDetailPage() {
|
|||
)}
|
||||
{deliverable.assetCount != null && (
|
||||
<div>
|
||||
<p className="text-[10px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
<p className="text-[15px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
Asset Count
|
||||
</p>
|
||||
<p>{deliverable.assetCount}</p>
|
||||
|
|
@ -176,7 +176,7 @@ export default function DeliverableDetailPage() {
|
|||
)}
|
||||
{deliverable.requestedDueDate && (
|
||||
<div>
|
||||
<p className="text-[10px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
<p className="text-[15px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
Requested Due Date
|
||||
</p>
|
||||
<p>{format(new Date(deliverable.requestedDueDate), "MMM d, yyyy")}</p>
|
||||
|
|
@ -184,7 +184,7 @@ export default function DeliverableDetailPage() {
|
|||
)}
|
||||
{deliverable.plannedDeliveryDate && (
|
||||
<div>
|
||||
<p className="text-[10px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
<p className="text-[15px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
Planned Delivery
|
||||
</p>
|
||||
<p>{format(new Date(deliverable.plannedDeliveryDate), "MMM d, yyyy")}</p>
|
||||
|
|
@ -192,7 +192,7 @@ export default function DeliverableDetailPage() {
|
|||
)}
|
||||
{deliverable.actualDeliveryDate && (
|
||||
<div>
|
||||
<p className="text-[10px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
<p className="text-[15px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
Actual Delivery
|
||||
</p>
|
||||
<p>{format(new Date(deliverable.actualDeliveryDate), "MMM d, yyyy")}</p>
|
||||
|
|
@ -200,7 +200,7 @@ export default function DeliverableDetailPage() {
|
|||
)}
|
||||
{deliverable.wfInputDate && (
|
||||
<div>
|
||||
<p className="text-[10px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
<p className="text-[15px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
WF Input Date
|
||||
</p>
|
||||
<p>{format(new Date(deliverable.wfInputDate), "MMM d, yyyy")}</p>
|
||||
|
|
@ -261,14 +261,14 @@ export default function DeliverableDetailPage() {
|
|||
|
||||
{/* Row 1 — step number · name · badges | status */}
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-mono text-[11px] tabular-nums text-[var(--muted-foreground)] min-w-[1.5rem]">
|
||||
<span className="font-mono text-[17px] tabular-nums text-[var(--muted-foreground)] min-w-[1.5rem]">
|
||||
{stageOrder}.
|
||||
</span>
|
||||
<span className="text-sm font-semibold">{stageName}</span>
|
||||
{isGate && (
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="h-4 gap-0.5 border-[var(--accent)] text-[9px] text-[var(--accent)] px-1"
|
||||
className="h-4 gap-0.5 border-[var(--accent)] text-[14px] text-[var(--accent)] px-1"
|
||||
>
|
||||
<Shield className="h-2.5 w-2.5" />
|
||||
GATE
|
||||
|
|
@ -277,7 +277,7 @@ export default function DeliverableDetailPage() {
|
|||
{isOptional && (
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="h-4 text-[9px] text-[var(--muted-foreground)] px-1"
|
||||
className="h-4 text-[14px] text-[var(--muted-foreground)] px-1"
|
||||
>
|
||||
Optional
|
||||
</Badge>
|
||||
|
|
@ -327,7 +327,7 @@ export default function DeliverableDetailPage() {
|
|||
size="sm"
|
||||
variant={isSkip || isReopen ? "outline" : "default"}
|
||||
className={cn(
|
||||
"h-6 text-[11px] px-2",
|
||||
"h-6 text-[17px] px-2",
|
||||
(isSkip || isReopen) && "text-[var(--muted-foreground)]"
|
||||
)}
|
||||
disabled={updateStage.isPending}
|
||||
|
|
@ -350,12 +350,12 @@ export default function DeliverableDetailPage() {
|
|||
|
||||
{/* Sub-status + blocked hint on their own third line only when present */}
|
||||
{stage.subStatus && (
|
||||
<div className="mt-1 text-[11px] italic text-[var(--muted-foreground)]">
|
||||
<div className="mt-1 text-[17px] italic text-[var(--muted-foreground)]">
|
||||
{stage.subStatus}
|
||||
</div>
|
||||
)}
|
||||
{stage.status === "BLOCKED" && (
|
||||
<div className="mt-1 flex items-center gap-1 text-[11px] text-[var(--status-blocked)]">
|
||||
<div className="mt-1 flex items-center gap-1 text-[17px] text-[var(--status-blocked)]">
|
||||
<Lock className="h-3 w-3" />
|
||||
Waiting for prerequisite stages
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ const PRIORITY_STYLES: Record<string, string> = {
|
|||
function MetaField({ label, value }: { label: string; value: string }) {
|
||||
return (
|
||||
<div>
|
||||
<p className="text-[10px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
<p className="text-[15px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
{label}
|
||||
</p>
|
||||
<p className="text-sm">{value}</p>
|
||||
|
|
@ -280,7 +280,7 @@ export default function ProjectDetailPage() {
|
|||
<Badge
|
||||
key={a.id}
|
||||
variant="secondary"
|
||||
className="text-[10px]"
|
||||
className="text-[15px]"
|
||||
>
|
||||
{a.user?.name ?? a.user?.email ?? "Unknown"}
|
||||
</Badge>
|
||||
|
|
|
|||
|
|
@ -345,7 +345,7 @@ export default function ProjectsPage() {
|
|||
</SelectContent>
|
||||
</Select>
|
||||
)}
|
||||
<div className="ml-auto text-[11px] tabular-nums text-[var(--muted-foreground)]">
|
||||
<div className="ml-auto text-[17px] tabular-nums text-[var(--muted-foreground)]">
|
||||
{filtered.length}
|
||||
{projects && filtered.length !== projects.length
|
||||
? ` of ${projects.length}`
|
||||
|
|
@ -368,7 +368,7 @@ export default function ProjectsPage() {
|
|||
<div className="overflow-auto rounded-lg border bg-[var(--card)]">
|
||||
<table className="w-full min-w-[1100px] text-xs tabular-nums">
|
||||
<thead>
|
||||
<tr className="border-b bg-[var(--muted)]/30 text-left text-[10px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
<tr className="border-b bg-[var(--muted)]/30 text-left text-[15px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
<Th label="Owner" sortKey="owner" sort={sort} onSort={toggleSort} />
|
||||
<Th label="Risk" sortKey="priority" sort={sort} onSort={toggleSort} />
|
||||
<Th label="OMG #" sortKey="omgJobNumber" sort={sort} onSort={toggleSort} />
|
||||
|
|
@ -456,19 +456,19 @@ function ProjectRow({ project, onDelete }: { project: Project; onDelete: () => v
|
|||
<td className="px-2 py-1.5">
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className={cn("h-4 px-1.5 text-[9px]", PRIORITY_STYLES[project.priority])}
|
||||
className={cn("h-4 px-1.5 text-[14px]", PRIORITY_STYLES[project.priority])}
|
||||
>
|
||||
{project.priority}
|
||||
</Badge>
|
||||
</td>
|
||||
<td className="px-2 py-1.5 font-mono text-[11px]">
|
||||
<td className="px-2 py-1.5 font-mono text-[17px]">
|
||||
{project.omgJobNumber ?? (
|
||||
<span className="text-[var(--muted-foreground)]">—</span>
|
||||
)}
|
||||
</td>
|
||||
<td className="px-2 py-1.5">
|
||||
{project.clientTeam ? (
|
||||
<span className="rounded-md bg-[var(--muted)] px-1.5 py-0.5 text-[10px] font-medium">
|
||||
<span className="rounded-md bg-[var(--muted)] px-1.5 py-0.5 text-[15px] font-medium">
|
||||
{project.clientTeam.name}
|
||||
</span>
|
||||
) : (
|
||||
|
|
@ -478,7 +478,7 @@ function ProjectRow({ project, onDelete }: { project: Project; onDelete: () => v
|
|||
<td className="px-2 py-1.5">
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className={cn("h-4 px-1.5 text-[9px]", STATUS_STYLES[project.status])}
|
||||
className={cn("h-4 px-1.5 text-[14px]", STATUS_STYLES[project.status])}
|
||||
>
|
||||
{project.status.replace("_", " ")}
|
||||
</Badge>
|
||||
|
|
@ -551,7 +551,7 @@ function PipelineProgressCell({ progress }: { progress?: PipelineProgress }) {
|
|||
|
||||
if (completedDeliverables === totalDeliverables && totalDeliverables > 0) {
|
||||
return (
|
||||
<span className="inline-flex items-center gap-1 rounded-md bg-emerald-100 px-1.5 py-0.5 text-[10px] font-semibold text-emerald-700 dark:bg-emerald-900 dark:text-emerald-300">
|
||||
<span className="inline-flex items-center gap-1 rounded-md bg-emerald-100 px-1.5 py-0.5 text-[15px] font-semibold text-emerald-700 dark:bg-emerald-900 dark:text-emerald-300">
|
||||
✓ Done
|
||||
</span>
|
||||
);
|
||||
|
|
@ -565,7 +565,7 @@ function PipelineProgressCell({ progress }: { progress?: PipelineProgress }) {
|
|||
|
||||
return (
|
||||
<div className="flex min-w-[140px] flex-col gap-0.5">
|
||||
<div className="flex items-center gap-1.5 text-[10px]">
|
||||
<div className="flex items-center gap-1.5 text-[15px]">
|
||||
<span className="font-semibold">{dominantStage.name}</span>
|
||||
<span className="text-[var(--muted-foreground)] tabular-nums">
|
||||
{dominantStage.count}/{totalDeliverables}
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ export default function WeeklyReportPage() {
|
|||
<p className="label-upper text-[var(--primary)]">
|
||||
Dow Jones Studio Tracker
|
||||
</p>
|
||||
<p className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<p className="text-[15px] text-[var(--muted-foreground)]">
|
||||
Confidential — Oliver Agency
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -289,7 +289,7 @@ export default function ResourcesPage() {
|
|||
{/* Filter bar */}
|
||||
<div className="flex flex-wrap items-center gap-2 rounded-lg border bg-[var(--card)] p-3">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<span className="text-[10px] font-bold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
<span className="text-[15px] font-bold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
Role
|
||||
</span>
|
||||
<Select value={roleFilter} onValueChange={setRoleFilter}>
|
||||
|
|
@ -307,7 +307,7 @@ export default function ResourcesPage() {
|
|||
</div>
|
||||
|
||||
<div className="flex items-center gap-1.5">
|
||||
<span className="text-[10px] font-bold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
<span className="text-[15px] font-bold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
Team
|
||||
</span>
|
||||
<Select value={teamFilter} onValueChange={setTeamFilter}>
|
||||
|
|
@ -326,7 +326,7 @@ export default function ResourcesPage() {
|
|||
</div>
|
||||
|
||||
<div className="flex items-center gap-1.5">
|
||||
<span className="text-[10px] font-bold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
<span className="text-[15px] font-bold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
Group by
|
||||
</span>
|
||||
<Select value={groupBy} onValueChange={(v) => setGroupBy(v as GroupBy)}>
|
||||
|
|
@ -343,7 +343,7 @@ export default function ResourcesPage() {
|
|||
</div>
|
||||
|
||||
<div className="flex items-center gap-1.5">
|
||||
<span className="text-[10px] font-bold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
<span className="text-[15px] font-bold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
Sort
|
||||
</span>
|
||||
<Select
|
||||
|
|
@ -393,7 +393,7 @@ export default function ResourcesPage() {
|
|||
</colgroup>
|
||||
<thead className="sticky top-0 z-10 bg-[var(--muted)]/60">
|
||||
<tr className="border-b-2 border-[var(--border)]">
|
||||
<th className="px-3 py-2 text-left text-[10px] font-bold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
<th className="px-3 py-2 text-left text-[15px] font-bold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
Resource
|
||||
</th>
|
||||
{weekDays.map((d) => {
|
||||
|
|
@ -403,7 +403,7 @@ export default function ResourcesPage() {
|
|||
key={d.toISOString()}
|
||||
className="px-2 py-2 text-center"
|
||||
>
|
||||
<div className="text-[10px] font-bold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
<div className="text-[15px] font-bold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
{format(d, "EEE")}
|
||||
</div>
|
||||
<div
|
||||
|
|
@ -417,7 +417,7 @@ export default function ResourcesPage() {
|
|||
</th>
|
||||
);
|
||||
})}
|
||||
<th className="px-2 py-2 text-center text-[10px] font-bold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
<th className="px-2 py-2 text-center text-[15px] font-bold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
Week
|
||||
</th>
|
||||
</tr>
|
||||
|
|
@ -484,14 +484,14 @@ export default function ResourcesPage() {
|
|||
>
|
||||
<td className="border-r border-[var(--border)] px-3 py-2.5 align-top">
|
||||
<div className="flex items-center gap-2.5">
|
||||
<div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-gradient-to-br from-[var(--primary)] to-[var(--primary)]/70 text-[11px] font-bold text-white">
|
||||
<div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-gradient-to-br from-[var(--primary)] to-[var(--primary)]/70 text-[17px] font-bold text-white">
|
||||
{initialsOf(user.name, user.email)}
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<div className="truncate text-xs font-semibold">
|
||||
{user.name ?? user.email}
|
||||
</div>
|
||||
<div className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<div className="text-[15px] text-[var(--muted-foreground)]">
|
||||
{user.maxCapacity}h/day
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -531,7 +531,7 @@ export default function ResourcesPage() {
|
|||
date: day,
|
||||
});
|
||||
}}
|
||||
className="flex items-center gap-1 rounded border border-dashed border-[var(--border)] px-1.5 py-0.5 text-[10px] font-semibold text-[var(--muted-foreground)] transition-colors hover:border-[var(--primary)] hover:text-[var(--primary)]"
|
||||
className="flex items-center gap-1 rounded border border-dashed border-[var(--border)] px-1.5 py-0.5 text-[15px] font-semibold text-[var(--muted-foreground)] transition-colors hover:border-[var(--primary)] hover:text-[var(--primary)]"
|
||||
>
|
||||
<Plus className="h-2.5 w-2.5" />
|
||||
Assign
|
||||
|
|
@ -556,7 +556,7 @@ export default function ResourcesPage() {
|
|||
>
|
||||
{weekTotal}h
|
||||
</div>
|
||||
<div className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<div className="text-[15px] text-[var(--muted-foreground)]">
|
||||
/ {weekCap}h
|
||||
</div>
|
||||
<div className="mt-1 h-1 overflow-hidden rounded bg-[var(--border)]">
|
||||
|
|
@ -617,7 +617,7 @@ function FragmentRows({
|
|||
>
|
||||
<td
|
||||
colSpan={7}
|
||||
className="border-b border-[var(--border)] px-3 py-1.5 text-[10px] font-bold uppercase tracking-wider text-[var(--muted-foreground)]"
|
||||
className="border-b border-[var(--border)] px-3 py-1.5 text-[15px] font-bold uppercase tracking-wider text-[var(--muted-foreground)]"
|
||||
>
|
||||
<span className="mr-2 opacity-60">{collapsed ? "▶" : "▼"}</span>
|
||||
{group} · {count} {count === 1 ? "person" : "people"}
|
||||
|
|
@ -642,7 +642,7 @@ function JobChip({
|
|||
const color = jobColor(jobNumber);
|
||||
return (
|
||||
<div
|
||||
className="group flex items-center gap-1 rounded border px-1.5 py-0.5 text-[10px] font-semibold tabular-nums transition-colors"
|
||||
className="group flex items-center gap-1 rounded border px-1.5 py-0.5 text-[15px] font-semibold tabular-nums transition-colors"
|
||||
style={{
|
||||
borderColor: `${color}55`,
|
||||
background: `${color}15`,
|
||||
|
|
@ -687,7 +687,7 @@ function CapBar({ booked, capacity }: { booked: number; capacity: number }) {
|
|||
</div>
|
||||
<div
|
||||
className={cn(
|
||||
"mt-0.5 text-[9px] font-bold tabular-nums",
|
||||
"mt-0.5 text-[14px] font-bold tabular-nums",
|
||||
over ? "text-red-600" : warn ? "text-amber-600" : "text-emerald-600"
|
||||
)}
|
||||
>
|
||||
|
|
@ -761,7 +761,7 @@ function AssignPopover({
|
|||
</div>
|
||||
<div className="flex min-h-0 flex-1 flex-col gap-3 p-3">
|
||||
<div className="flex min-h-0 flex-col">
|
||||
<label className="mb-1 block text-[10px] font-bold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
<label className="mb-1 block text-[15px] font-bold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
Job Number
|
||||
</label>
|
||||
<Input
|
||||
|
|
@ -778,19 +778,19 @@ function AssignPopover({
|
|||
producers can pick by context, not just by number. */}
|
||||
<div className="mt-2 min-h-0 flex-1 overflow-y-auto rounded border">
|
||||
{knownJobs.length === 0 ? (
|
||||
<div className="px-3 py-4 text-center text-[10px] text-[var(--muted-foreground)]">
|
||||
<div className="px-3 py-4 text-center text-[15px] text-[var(--muted-foreground)]">
|
||||
No known jobs yet.
|
||||
<br />
|
||||
Upload the XLSX tracker in <span className="font-semibold">Projects → Import XLSX</span> first,
|
||||
or type a freeform number above.
|
||||
</div>
|
||||
) : suggestions.length === 0 ? (
|
||||
<div className="px-3 py-4 text-center text-[10px] text-[var(--muted-foreground)]">
|
||||
<div className="px-3 py-4 text-center text-[15px] text-[var(--muted-foreground)]">
|
||||
No matches for “{jobNumber}”. Press Enter to assign it anyway.
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<div className="sticky top-0 border-b bg-[var(--muted)]/40 px-2 py-1 text-[9px] font-bold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
<div className="sticky top-0 border-b bg-[var(--muted)]/40 px-2 py-1 text-[14px] font-bold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
{suggestions.length} available
|
||||
</div>
|
||||
{suggestions.map((j) => {
|
||||
|
|
@ -814,12 +814,12 @@ function AssignPopover({
|
|||
style={{ background: color }}
|
||||
/>
|
||||
<span
|
||||
className="w-16 shrink-0 font-mono text-[11px] font-bold tabular-nums"
|
||||
className="w-16 shrink-0 font-mono text-[17px] font-bold tabular-nums"
|
||||
style={{ color }}
|
||||
>
|
||||
{j.jobNumber}
|
||||
</span>
|
||||
<span className="flex-1 truncate text-[10px] text-[var(--foreground)]">
|
||||
<span className="flex-1 truncate text-[15px] text-[var(--foreground)]">
|
||||
{j.name}
|
||||
</span>
|
||||
</button>
|
||||
|
|
@ -830,7 +830,7 @@ function AssignPopover({
|
|||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label className="mb-1 block text-[10px] font-bold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
<label className="mb-1 block text-[15px] font-bold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
Hours
|
||||
</label>
|
||||
<div className="flex gap-1">
|
||||
|
|
@ -851,7 +851,7 @@ function AssignPopover({
|
|||
))}
|
||||
</div>
|
||||
<div className="mt-1.5 flex items-center gap-1.5">
|
||||
<span className="text-[10px] text-[var(--muted-foreground)]">Custom:</span>
|
||||
<span className="text-[15px] text-[var(--muted-foreground)]">Custom:</span>
|
||||
<Input
|
||||
type="number"
|
||||
min={0.5}
|
||||
|
|
@ -864,11 +864,11 @@ function AssignPopover({
|
|||
placeholder="—"
|
||||
className="h-6 w-16 text-xs"
|
||||
/>
|
||||
<span className="text-[10px] text-[var(--muted-foreground)]">hrs</span>
|
||||
<span className="text-[15px] text-[var(--muted-foreground)]">hrs</span>
|
||||
</div>
|
||||
</div>
|
||||
{hours > 8 && (
|
||||
<div className="flex items-center gap-1.5 rounded border border-amber-400/40 bg-amber-400/10 px-2 py-1 text-[10px] text-amber-700 dark:text-amber-400">
|
||||
<div className="flex items-center gap-1.5 rounded border border-amber-400/40 bg-amber-400/10 px-2 py-1 text-[15px] text-amber-700 dark:text-amber-400">
|
||||
<AlertCircle className="h-3 w-3" />
|
||||
{hours}h will likely push this person over their daily cap.
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -245,7 +245,7 @@ function ActionRow({
|
|||
{/* Action-specific params */}
|
||||
{action.type === "update_stage_status" && (
|
||||
<div className="space-y-1.5">
|
||||
<Label className="label-upper text-[9px]">New Status</Label>
|
||||
<Label className="label-upper text-[14px]">New Status</Label>
|
||||
<Select
|
||||
value={action.params.status || ""}
|
||||
onValueChange={(v) => updateParam("status", v)}
|
||||
|
|
@ -267,7 +267,7 @@ function ActionRow({
|
|||
{action.type === "send_notification" && (
|
||||
<div className="space-y-2">
|
||||
<div className="space-y-1.5">
|
||||
<Label className="label-upper text-[9px]">Title</Label>
|
||||
<Label className="label-upper text-[14px]">Title</Label>
|
||||
<Input
|
||||
className="h-8 text-xs"
|
||||
placeholder="e.g. {stageName} approved"
|
||||
|
|
@ -276,7 +276,7 @@ function ActionRow({
|
|||
/>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="label-upper text-[9px]">Message</Label>
|
||||
<Label className="label-upper text-[14px]">Message</Label>
|
||||
<Textarea
|
||||
className="min-h-[60px] text-xs"
|
||||
placeholder="Use {fieldName} for event data..."
|
||||
|
|
@ -285,7 +285,7 @@ function ActionRow({
|
|||
/>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="label-upper text-[9px]">Notify Roles</Label>
|
||||
<Label className="label-upper text-[14px]">Notify Roles</Label>
|
||||
<div className="flex gap-3">
|
||||
{["ADMIN", "PRODUCER", "ARTIST"].map((role) => (
|
||||
<label
|
||||
|
|
@ -310,7 +310,7 @@ function ActionRow({
|
|||
</div>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="label-upper text-[9px]">
|
||||
<Label className="label-upper text-[14px]">
|
||||
Link (optional)
|
||||
</Label>
|
||||
<Input
|
||||
|
|
@ -326,7 +326,7 @@ function ActionRow({
|
|||
{action.type === "create_assignment" && (
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Label className="label-upper text-[9px]">
|
||||
<Label className="label-upper text-[14px]">
|
||||
Auto (skill-based)
|
||||
</Label>
|
||||
<Switch
|
||||
|
|
@ -338,7 +338,7 @@ function ActionRow({
|
|||
</div>
|
||||
{action.params.userId !== "auto" && (
|
||||
<div className="space-y-1.5">
|
||||
<Label className="label-upper text-[9px]">User ID</Label>
|
||||
<Label className="label-upper text-[14px]">User ID</Label>
|
||||
<Input
|
||||
className="h-8 text-xs"
|
||||
placeholder="User ID to assign..."
|
||||
|
|
@ -352,14 +352,14 @@ function ActionRow({
|
|||
|
||||
{action.type === "send_webhook" && (
|
||||
<div className="space-y-1.5">
|
||||
<Label className="label-upper text-[9px]">Webhook URL</Label>
|
||||
<Label className="label-upper text-[14px]">Webhook URL</Label>
|
||||
<Input
|
||||
className="h-8 text-xs"
|
||||
placeholder="https://..."
|
||||
value={action.params.url || ""}
|
||||
onChange={(e) => updateParam("url", e.target.value)}
|
||||
/>
|
||||
<p className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<p className="text-[15px] text-[var(--muted-foreground)]">
|
||||
Microsoft Teams incoming webhook URLs are auto-detected and
|
||||
formatted as Adaptive Cards.
|
||||
</p>
|
||||
|
|
@ -546,12 +546,12 @@ export default function AutomationRulesPage() {
|
|||
</span>
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="text-[9px] h-4 px-1.5 shrink-0"
|
||||
className="text-[14px] h-4 px-1.5 shrink-0"
|
||||
>
|
||||
{getEventLabel(trigger?.event)}
|
||||
</Badge>
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5 text-[10px] text-[var(--muted-foreground)] flex-wrap">
|
||||
<div className="flex items-center gap-1.5 text-[15px] text-[var(--muted-foreground)] flex-wrap">
|
||||
{condCount > 0 && (
|
||||
<span>
|
||||
{condCount} condition{condCount !== 1 ? "s" : ""}
|
||||
|
|
@ -562,7 +562,7 @@ export default function AutomationRulesPage() {
|
|||
<Badge
|
||||
key={i}
|
||||
variant="secondary"
|
||||
className="text-[9px] h-4 px-1.5"
|
||||
className="text-[14px] h-4 px-1.5"
|
||||
>
|
||||
{getActionLabel(a.type)}
|
||||
</Badge>
|
||||
|
|
@ -655,7 +655,7 @@ export default function AutomationRulesPage() {
|
|||
<Badge
|
||||
variant="secondary"
|
||||
className={cn(
|
||||
"text-[9px] h-4 px-1.5 shrink-0",
|
||||
"text-[14px] h-4 px-1.5 shrink-0",
|
||||
statusBadgeClass(exec.status)
|
||||
)}
|
||||
>
|
||||
|
|
@ -663,7 +663,7 @@ export default function AutomationRulesPage() {
|
|||
</Badge>
|
||||
</button>
|
||||
{expandedExec === exec.id && (
|
||||
<div className="ml-8 mb-2 rounded border bg-[var(--muted)]/30 p-2 text-[10px]">
|
||||
<div className="ml-8 mb-2 rounded border bg-[var(--muted)]/30 p-2 text-[15px]">
|
||||
<div className="space-y-1">
|
||||
<div>
|
||||
<span className="font-medium">Event:</span>{" "}
|
||||
|
|
@ -679,7 +679,7 @@ export default function AutomationRulesPage() {
|
|||
<summary className="font-medium text-[var(--muted-foreground)]">
|
||||
Payload & Results
|
||||
</summary>
|
||||
<pre className="mt-1 overflow-auto rounded bg-[var(--muted)] p-1.5 text-[9px] max-h-[200px]">
|
||||
<pre className="mt-1 overflow-auto rounded bg-[var(--muted)] p-1.5 text-[14px] max-h-[200px]">
|
||||
{JSON.stringify(
|
||||
{
|
||||
payload: (exec.triggeredBy as any)?.payload,
|
||||
|
|
@ -715,7 +715,7 @@ export default function AutomationRulesPage() {
|
|||
<div className="space-y-5">
|
||||
{/* Name */}
|
||||
<div className="space-y-1.5">
|
||||
<Label className="label-upper text-[10px]">Name</Label>
|
||||
<Label className="label-upper text-[15px]">Name</Label>
|
||||
<Input
|
||||
placeholder="e.g. Auto-notify on approval"
|
||||
value={formName}
|
||||
|
|
@ -726,7 +726,7 @@ export default function AutomationRulesPage() {
|
|||
|
||||
{/* Description */}
|
||||
<div className="space-y-1.5">
|
||||
<Label className="label-upper text-[10px]">
|
||||
<Label className="label-upper text-[15px]">
|
||||
Description (optional)
|
||||
</Label>
|
||||
<Input
|
||||
|
|
@ -739,7 +739,7 @@ export default function AutomationRulesPage() {
|
|||
|
||||
{/* Trigger Event */}
|
||||
<div className="space-y-1.5">
|
||||
<Label className="label-upper text-[10px]">
|
||||
<Label className="label-upper text-[15px]">
|
||||
Trigger Event
|
||||
</Label>
|
||||
<Select
|
||||
|
|
@ -765,7 +765,7 @@ export default function AutomationRulesPage() {
|
|||
{/* Conditions */}
|
||||
{formEvent && (
|
||||
<div className="space-y-2">
|
||||
<Label className="label-upper text-[10px]">
|
||||
<Label className="label-upper text-[15px]">
|
||||
Conditions{" "}
|
||||
<span className="font-normal normal-case text-[var(--muted-foreground)]">
|
||||
(all must match)
|
||||
|
|
@ -799,7 +799,7 @@ export default function AutomationRulesPage() {
|
|||
|
||||
{/* Actions */}
|
||||
<div className="space-y-2">
|
||||
<Label className="label-upper text-[10px]">Actions</Label>
|
||||
<Label className="label-upper text-[15px]">Actions</Label>
|
||||
{formActions.map((action, i) => (
|
||||
<ActionRow
|
||||
key={i}
|
||||
|
|
@ -826,7 +826,7 @@ export default function AutomationRulesPage() {
|
|||
|
||||
{/* Enabled */}
|
||||
<div className="flex items-center justify-between">
|
||||
<Label className="label-upper text-[10px]">Enabled</Label>
|
||||
<Label className="label-upper text-[15px]">Enabled</Label>
|
||||
<Switch checked={formEnabled} onCheckedChange={setFormEnabled} />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ export default function ClientTeamsSettingsPage() {
|
|||
<div className="flex items-center gap-2">
|
||||
<Users className="h-4 w-4" />
|
||||
<span>{team.name}</span>
|
||||
<Badge variant="outline" className="h-4 px-1 text-[9px]">
|
||||
<Badge variant="outline" className="h-4 px-1 text-[14px]">
|
||||
{team.slug}
|
||||
</Badge>
|
||||
</div>
|
||||
|
|
@ -171,7 +171,7 @@ export default function ClientTeamsSettingsPage() {
|
|||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-3">
|
||||
<div className="flex gap-3 text-[11px] text-[var(--muted-foreground)]">
|
||||
<div className="flex gap-3 text-[17px] text-[var(--muted-foreground)]">
|
||||
<span>
|
||||
{team._count?.userMemberships ?? 0} member
|
||||
{(team._count?.userMemberships ?? 0) !== 1 ? "s" : ""}
|
||||
|
|
@ -221,7 +221,7 @@ export default function ClientTeamsSettingsPage() {
|
|||
|
||||
<div className="space-y-1">
|
||||
{(team.userMemberships ?? []).length === 0 ? (
|
||||
<p className="py-2 text-[11px] text-[var(--muted-foreground)]">
|
||||
<p className="py-2 text-[17px] text-[var(--muted-foreground)]">
|
||||
No members. Users without a team membership won't see any
|
||||
projects on this team.
|
||||
</p>
|
||||
|
|
@ -236,12 +236,12 @@ export default function ClientTeamsSettingsPage() {
|
|||
{m.user.isExternal && (
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="h-4 px-1 text-[9px] text-amber-600"
|
||||
className="h-4 px-1 text-[14px] text-amber-600"
|
||||
>
|
||||
client
|
||||
</Badge>
|
||||
)}
|
||||
<span className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<span className="text-[15px] text-[var(--muted-foreground)]">
|
||||
{m.user.email}
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -203,7 +203,7 @@ function FieldSection({ entityType, label }: { entityType: EntityType; label: st
|
|||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<label className="flex shrink-0 items-center gap-1 text-[10px] text-[var(--muted-foreground)]">
|
||||
<label className="flex shrink-0 items-center gap-1 text-[15px] text-[var(--muted-foreground)]">
|
||||
<Checkbox
|
||||
checked={editRequired}
|
||||
onCheckedChange={(v) => setEditRequired(v === true)}
|
||||
|
|
@ -236,19 +236,19 @@ function FieldSection({ entityType, label }: { entityType: EntityType; label: st
|
|||
<span className="text-sm font-medium">{field.fieldName}</span>
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className={cn("text-[9px] h-4 px-1.5", getFieldTypeStyle(field.fieldType))}
|
||||
className={cn("text-[14px] h-4 px-1.5", getFieldTypeStyle(field.fieldType))}
|
||||
>
|
||||
{getFieldTypeLabel(field.fieldType)}
|
||||
</Badge>
|
||||
{field.isRequired && (
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="text-[9px] h-4 px-1 text-red-500 border-red-200 dark:border-red-800"
|
||||
className="text-[14px] h-4 px-1 text-red-500 border-red-200 dark:border-red-800"
|
||||
>
|
||||
Required
|
||||
</Badge>
|
||||
)}
|
||||
<span className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<span className="text-[15px] text-[var(--muted-foreground)]">
|
||||
#{field.order}
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ export default function NotificationRulesPage() {
|
|||
<span className="text-sm font-medium truncate">
|
||||
{rule.name}
|
||||
</span>
|
||||
<Badge variant="outline" className="text-[9px] h-4 px-1.5 shrink-0">
|
||||
<Badge variant="outline" className="text-[14px] h-4 px-1.5 shrink-0">
|
||||
{getEventLabel(rule.event)}
|
||||
</Badge>
|
||||
</div>
|
||||
|
|
@ -200,7 +200,7 @@ export default function NotificationRulesPage() {
|
|||
key={ch}
|
||||
variant="secondary"
|
||||
className={cn(
|
||||
"text-[9px] h-4 px-1.5",
|
||||
"text-[14px] h-4 px-1.5",
|
||||
ch === "IN_APP"
|
||||
? "bg-blue-100 text-blue-700 dark:bg-blue-900 dark:text-blue-300"
|
||||
: "bg-amber-100 text-amber-700 dark:bg-amber-900 dark:text-amber-300"
|
||||
|
|
@ -213,7 +213,7 @@ export default function NotificationRulesPage() {
|
|||
<Badge
|
||||
key={role}
|
||||
variant="outline"
|
||||
className="text-[9px] h-4 px-1.5"
|
||||
className="text-[14px] h-4 px-1.5"
|
||||
>
|
||||
{role}
|
||||
</Badge>
|
||||
|
|
@ -257,7 +257,7 @@ export default function NotificationRulesPage() {
|
|||
<div className="space-y-4">
|
||||
{/* Name */}
|
||||
<div className="space-y-1.5">
|
||||
<Label className="label-upper text-[10px]">Name</Label>
|
||||
<Label className="label-upper text-[15px]">Name</Label>
|
||||
<Input
|
||||
placeholder="Rule name..."
|
||||
value={formName}
|
||||
|
|
@ -268,7 +268,7 @@ export default function NotificationRulesPage() {
|
|||
|
||||
{/* Event */}
|
||||
<div className="space-y-1.5">
|
||||
<Label className="label-upper text-[10px]">Event</Label>
|
||||
<Label className="label-upper text-[15px]">Event</Label>
|
||||
<Select value={formEvent} onValueChange={setFormEvent}>
|
||||
<SelectTrigger className="h-8 text-sm">
|
||||
<SelectValue placeholder="Select an event..." />
|
||||
|
|
@ -285,7 +285,7 @@ export default function NotificationRulesPage() {
|
|||
|
||||
{/* Channels */}
|
||||
<div className="space-y-1.5">
|
||||
<Label className="label-upper text-[10px]">Channels</Label>
|
||||
<Label className="label-upper text-[15px]">Channels</Label>
|
||||
<div className="flex gap-4">
|
||||
{CHANNELS.map((ch) => (
|
||||
<label
|
||||
|
|
@ -304,7 +304,7 @@ export default function NotificationRulesPage() {
|
|||
|
||||
{/* Recipient Roles */}
|
||||
<div className="space-y-1.5">
|
||||
<Label className="label-upper text-[10px]">
|
||||
<Label className="label-upper text-[15px]">
|
||||
Recipient Roles
|
||||
</Label>
|
||||
<div className="flex flex-wrap gap-4">
|
||||
|
|
@ -325,7 +325,7 @@ export default function NotificationRulesPage() {
|
|||
|
||||
{/* Enabled toggle */}
|
||||
<div className="flex items-center justify-between">
|
||||
<Label className="label-upper text-[10px]">Enabled</Label>
|
||||
<Label className="label-upper text-[15px]">Enabled</Label>
|
||||
<Switch checked={formEnabled} onCheckedChange={setFormEnabled} />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -157,19 +157,19 @@ export default function PermissionsPage() {
|
|||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b">
|
||||
<th className="label-upper pb-2 pr-4 text-left text-[10px] tracking-wider text-[var(--muted-foreground)]">
|
||||
<th className="label-upper pb-2 pr-4 text-left text-[15px] tracking-wider text-[var(--muted-foreground)]">
|
||||
Permission
|
||||
</th>
|
||||
{ROLES.map((role) => (
|
||||
<th key={role} className="pb-2 px-3 text-center min-w-[100px]">
|
||||
<div className="flex flex-col items-center gap-1.5">
|
||||
<Badge className={cn("text-[10px] px-2", ROLE_COLORS[role])}>
|
||||
<Badge className={cn("text-[15px] px-2", ROLE_COLORS[role])}>
|
||||
{role}
|
||||
</Badge>
|
||||
{dirty.has(role) && role !== "ADMIN" && (
|
||||
<Button
|
||||
size="sm"
|
||||
className="h-5 px-2 text-[10px]"
|
||||
className="h-5 px-2 text-[15px]"
|
||||
onClick={() => saveRole(role)}
|
||||
disabled={updatePerms.isPending}
|
||||
>
|
||||
|
|
@ -188,7 +188,7 @@ export default function PermissionsPage() {
|
|||
<tr>
|
||||
<td
|
||||
colSpan={ROLES.length + 1}
|
||||
className="label-upper pt-4 pb-1 text-[10px] font-semibold tracking-wider text-[var(--primary)]"
|
||||
className="label-upper pt-4 pb-1 text-[15px] font-semibold tracking-wider text-[var(--primary)]"
|
||||
>
|
||||
{group.label}
|
||||
</td>
|
||||
|
|
@ -252,7 +252,7 @@ export default function PermissionsPage() {
|
|||
|
||||
<div className="mt-4 flex items-center gap-2 rounded-lg border border-[var(--border)] bg-[var(--muted)]/30 px-3 py-2">
|
||||
<Shield className="h-3.5 w-3.5 text-purple-500 shrink-0" />
|
||||
<p className="text-[11px] text-[var(--muted-foreground)]">
|
||||
<p className="text-[17px] text-[var(--muted-foreground)]">
|
||||
Admin role always has all permissions and cannot be modified.
|
||||
Changes are saved per-role and take effect immediately.
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -192,7 +192,7 @@ export default function PipelineEditorPage({ params }: PageProps) {
|
|||
)}
|
||||
</div>
|
||||
{pl.isDefault && (
|
||||
<Badge className="bg-amber-100 text-amber-700 dark:bg-amber-900 dark:text-amber-300 text-[9px] h-4 px-1.5">
|
||||
<Badge className="bg-amber-100 text-amber-700 dark:bg-amber-900 dark:text-amber-300 text-[14px] h-4 px-1.5">
|
||||
<Star className="mr-0.5 h-2.5 w-2.5" />
|
||||
Default
|
||||
</Badge>
|
||||
|
|
@ -208,7 +208,7 @@ export default function PipelineEditorPage({ params }: PageProps) {
|
|||
<Star className="mr-1 h-3 w-3" />
|
||||
{pl.isDefault ? "Remove Default" : "Set Default"}
|
||||
</Button>
|
||||
<span className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<span className="text-[15px] text-[var(--muted-foreground)]">
|
||||
{pl._count?.projects ?? 0} projects
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -227,10 +227,10 @@ export default function PipelineEditorPage({ params }: PageProps) {
|
|||
{/* Left: Stage list */}
|
||||
<div className="rounded-lg border bg-[var(--card)] p-3">
|
||||
<div className="mb-2 flex items-center justify-between">
|
||||
<span className="label-upper text-[10px] font-semibold tracking-wider text-[var(--muted-foreground)]">
|
||||
<span className="label-upper text-[15px] font-semibold tracking-wider text-[var(--muted-foreground)]">
|
||||
Stages
|
||||
</span>
|
||||
<span className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<span className="text-[15px] text-[var(--muted-foreground)]">
|
||||
{stages.length} total
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -246,7 +246,7 @@ export default function PipelineEditorPage({ params }: PageProps) {
|
|||
{/* Right: Dependency + rework graph */}
|
||||
<div className="rounded-lg border bg-[var(--card)] overflow-hidden" style={{ minHeight: 500 }}>
|
||||
<div className="flex items-center justify-between border-b px-3 py-2">
|
||||
<span className="label-upper text-[10px] font-semibold tracking-wider text-[var(--muted-foreground)]">
|
||||
<span className="label-upper text-[15px] font-semibold tracking-wider text-[var(--muted-foreground)]">
|
||||
Workflow Graph
|
||||
</span>
|
||||
<div className="flex items-center gap-0.5 rounded-md border bg-[var(--muted)]/40 p-0.5">
|
||||
|
|
@ -254,7 +254,7 @@ export default function PipelineEditorPage({ params }: PageProps) {
|
|||
type="button"
|
||||
size="sm"
|
||||
variant={graphMode === "dependency" ? "default" : "ghost"}
|
||||
className="h-6 gap-1 px-2 text-[10px]"
|
||||
className="h-6 gap-1 px-2 text-[15px]"
|
||||
onClick={() => setGraphMode("dependency")}
|
||||
title="Drag between stages to create forward dependencies"
|
||||
>
|
||||
|
|
@ -265,7 +265,7 @@ export default function PipelineEditorPage({ params }: PageProps) {
|
|||
type="button"
|
||||
size="sm"
|
||||
variant={graphMode === "rework" ? "default" : "ghost"}
|
||||
className="h-6 gap-1 px-2 text-[10px]"
|
||||
className="h-6 gap-1 px-2 text-[15px]"
|
||||
onClick={() => setGraphMode("rework")}
|
||||
title="Drag backward (higher → lower order) to allow pushing work back on failure"
|
||||
>
|
||||
|
|
@ -274,7 +274,7 @@ export default function PipelineEditorPage({ params }: PageProps) {
|
|||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="border-b bg-[var(--muted)]/20 px-3 py-1.5 text-[10px] text-[var(--muted-foreground)]">
|
||||
<div className="border-b bg-[var(--muted)]/20 px-3 py-1.5 text-[15px] text-[var(--muted-foreground)]">
|
||||
{graphMode === "dependency" ? (
|
||||
<>Drag from one stage to another to add a <strong>forward prerequisite</strong>. Click the × on a solid line to remove.</>
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ export default function PipelinesPage() {
|
|||
<CardTitle className="flex items-center gap-2 text-sm">
|
||||
{pipeline.name}
|
||||
{pipeline.isDefault && (
|
||||
<Badge className="bg-amber-100 text-amber-700 dark:bg-amber-900 dark:text-amber-300 text-[9px] h-4 px-1.5">
|
||||
<Badge className="bg-amber-100 text-amber-700 dark:bg-amber-900 dark:text-amber-300 text-[14px] h-4 px-1.5">
|
||||
<Star className="mr-0.5 h-2.5 w-2.5" />
|
||||
Default
|
||||
</Badge>
|
||||
|
|
@ -130,7 +130,7 @@ export default function PipelinesPage() {
|
|||
{pipeline.description}
|
||||
</p>
|
||||
)}
|
||||
<div className="flex items-center gap-3 text-[10px] text-[var(--muted-foreground)]">
|
||||
<div className="flex items-center gap-3 text-[15px] text-[var(--muted-foreground)]">
|
||||
<span className="label-upper">
|
||||
{pipeline.stages?.length ?? 0} stages
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ export default function PodsSettingsPage() {
|
|||
<div className="flex items-center gap-2">
|
||||
<Users2 className="h-4 w-4" />
|
||||
<span>{pod.name}</span>
|
||||
<Badge variant="outline" className="h-4 px-1 text-[9px]">
|
||||
<Badge variant="outline" className="h-4 px-1 text-[14px]">
|
||||
{pod.slug}
|
||||
</Badge>
|
||||
</div>
|
||||
|
|
@ -187,7 +187,7 @@ export default function PodsSettingsPage() {
|
|||
<CardContent className="space-y-3">
|
||||
{/* Lead selector */}
|
||||
<div className="space-y-1">
|
||||
<label className="text-[10px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
<label className="text-[15px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
Pod lead
|
||||
</label>
|
||||
<Select
|
||||
|
|
@ -247,7 +247,7 @@ export default function PodsSettingsPage() {
|
|||
{/* Member list */}
|
||||
<div className="space-y-1">
|
||||
{(pod.members ?? []).length === 0 ? (
|
||||
<p className="py-2 text-[11px] text-[var(--muted-foreground)]">
|
||||
<p className="py-2 text-[17px] text-[var(--muted-foreground)]">
|
||||
No members yet.
|
||||
</p>
|
||||
) : (
|
||||
|
|
@ -262,7 +262,7 @@ export default function PodsSettingsPage() {
|
|||
)}
|
||||
<span>{u.name || u.email}</span>
|
||||
{u.department && (
|
||||
<span className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<span className="text-[15px] text-[var(--muted-foreground)]">
|
||||
· {u.department}
|
||||
</span>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ export default function SkillsSettingsPage() {
|
|||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-sm font-medium">{skill.name}</span>
|
||||
<Badge variant="outline" className="text-[9px] h-4 px-1">
|
||||
<Badge variant="outline" className="text-[14px] h-4 px-1">
|
||||
<Users className="mr-0.5 h-2.5 w-2.5" />
|
||||
{skill._count?.users ?? 0}
|
||||
</Badge>
|
||||
|
|
@ -278,7 +278,7 @@ export default function SkillsSettingsPage() {
|
|||
{hasSkill && (
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className={cn("text-[9px] h-4 px-1.5", getSkillLevelStyle(currentLevel))}
|
||||
className={cn("text-[14px] h-4 px-1.5", getSkillLevelStyle(currentLevel))}
|
||||
>
|
||||
{SKILL_LEVELS.find((l) => l.value === currentLevel)?.label}
|
||||
</Badge>
|
||||
|
|
@ -294,7 +294,7 @@ export default function SkillsSettingsPage() {
|
|||
}
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="h-6 w-[110px] text-[10px] border-none bg-transparent">
|
||||
<SelectTrigger className="h-6 w-[110px] text-[15px] border-none bg-transparent">
|
||||
<SelectValue placeholder="Not set" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
|
|
|||
|
|
@ -168,12 +168,12 @@ export default function TeamSettingsPage() {
|
|||
</span>
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className={cn("text-[9px] h-4 shrink-0 px-1.5", ROLE_STYLES[user.role] || "")}
|
||||
className={cn("text-[14px] h-4 shrink-0 px-1.5", ROLE_STYLES[user.role] || "")}
|
||||
>
|
||||
{user.role}
|
||||
</Badge>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-[11px] text-[var(--muted-foreground)]">
|
||||
<div className="flex items-center gap-2 text-[17px] text-[var(--muted-foreground)]">
|
||||
<Mail className="h-3 w-3 shrink-0" />
|
||||
<span className="truncate">{user.email}</span>
|
||||
{user.department && (
|
||||
|
|
@ -235,30 +235,30 @@ export default function TeamSettingsPage() {
|
|||
{lastInvite && (
|
||||
<div className="rounded-md border border-[var(--primary)]/30 bg-[var(--primary)]/5 p-3">
|
||||
<div className="mb-1.5 flex items-center justify-between gap-2">
|
||||
<p className="text-[10px] font-semibold uppercase tracking-wider text-[var(--primary)]">
|
||||
<p className="text-[15px] font-semibold uppercase tracking-wider text-[var(--primary)]">
|
||||
Invite link for {lastInvite.email}
|
||||
</p>
|
||||
<button
|
||||
onClick={() => setLastInvite(null)}
|
||||
className="text-[10px] text-[var(--muted-foreground)] hover:text-[var(--foreground)]"
|
||||
className="text-[15px] text-[var(--muted-foreground)] hover:text-[var(--foreground)]"
|
||||
>
|
||||
dismiss
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<code className="flex-1 truncate rounded bg-[var(--background)] px-2 py-1 font-mono text-[11px]">
|
||||
<code className="flex-1 truncate rounded bg-[var(--background)] px-2 py-1 font-mono text-[17px]">
|
||||
{lastInvite.url}
|
||||
</code>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="h-7 shrink-0 text-[11px]"
|
||||
className="h-7 shrink-0 text-[17px]"
|
||||
onClick={copyAcceptUrl}
|
||||
>
|
||||
Copy
|
||||
</Button>
|
||||
</div>
|
||||
<p className="mt-1.5 text-[10px] text-[var(--muted-foreground)]">
|
||||
<p className="mt-1.5 text-[15px] text-[var(--muted-foreground)]">
|
||||
Share this link with the user — it lets them set a password
|
||||
and sign in. Valid for 7 days.
|
||||
</p>
|
||||
|
|
@ -293,19 +293,19 @@ export default function TeamSettingsPage() {
|
|||
<span className="truncate text-sm font-medium">{inv.email}</span>
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className={cn("text-[9px] h-4 shrink-0 px-1.5", ROLE_STYLES[inv.role] || "")}
|
||||
className={cn("text-[14px] h-4 shrink-0 px-1.5", ROLE_STYLES[inv.role] || "")}
|
||||
>
|
||||
{inv.role}
|
||||
</Badge>
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className={cn("text-[9px] h-4 shrink-0 px-1.5", status.className)}
|
||||
className={cn("text-[14px] h-4 shrink-0 px-1.5", status.className)}
|
||||
>
|
||||
<StatusIcon className="mr-0.5 h-2.5 w-2.5" />
|
||||
{status.label}
|
||||
</Badge>
|
||||
</div>
|
||||
<p className="text-[11px] text-[var(--muted-foreground)]">
|
||||
<p className="text-[17px] text-[var(--muted-foreground)]">
|
||||
Invited by {inv.invitedBy?.name || inv.invitedBy?.email || "Unknown"}
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ export default function TimelinePage() {
|
|||
<p className="text-xl font-bold">
|
||||
{isLoading ? "—" : totalProjects}
|
||||
</p>
|
||||
<p className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<p className="text-[15px] text-[var(--muted-foreground)]">
|
||||
Projects
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -124,7 +124,7 @@ export default function TimelinePage() {
|
|||
<p className="text-xl font-bold">
|
||||
{isLoading ? "—" : totalDeliverables}
|
||||
</p>
|
||||
<p className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<p className="text-[15px] text-[var(--muted-foreground)]">
|
||||
Deliverables
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -140,7 +140,7 @@ export default function TimelinePage() {
|
|||
<p className="text-xl font-bold">
|
||||
{isLoading ? "—" : `${avgCompletion}%`}
|
||||
</p>
|
||||
<p className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<p className="text-[15px] text-[var(--muted-foreground)]">
|
||||
Avg Completion
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -163,7 +163,7 @@ export default function TimelinePage() {
|
|||
<p className="text-xl font-bold">
|
||||
{isLoading ? "—" : totalOverdue}
|
||||
</p>
|
||||
<p className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<p className="text-[15px] text-[var(--muted-foreground)]">
|
||||
Overdue Deliverables
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ export default function WorkloadPage() {
|
|||
</div>
|
||||
<div>
|
||||
<p className="text-xl font-bold">{isLoading ? "—" : totalMembers}</p>
|
||||
<p className="text-[10px] text-[var(--muted-foreground)]">Team Members</p>
|
||||
<p className="text-[15px] text-[var(--muted-foreground)]">Team Members</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
|
@ -97,7 +97,7 @@ export default function WorkloadPage() {
|
|||
</div>
|
||||
<div>
|
||||
<p className="text-xl font-bold">{isLoading ? "—" : totalActiveAssignments}</p>
|
||||
<p className="text-[10px] text-[var(--muted-foreground)]">Active Assignments</p>
|
||||
<p className="text-[15px] text-[var(--muted-foreground)]">Active Assignments</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
|
@ -109,7 +109,7 @@ export default function WorkloadPage() {
|
|||
</div>
|
||||
<div>
|
||||
<p className="text-xl font-bold">{isLoading ? "—" : `${avgUtilization}%`}</p>
|
||||
<p className="text-[10px] text-[var(--muted-foreground)]">Avg Utilization</p>
|
||||
<p className="text-[15px] text-[var(--muted-foreground)]">Avg Utilization</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
|
@ -128,7 +128,7 @@ export default function WorkloadPage() {
|
|||
</div>
|
||||
<div>
|
||||
<p className="text-xl font-bold">{isLoading ? "—" : overloadedCount}</p>
|
||||
<p className="text-[10px] text-[var(--muted-foreground)]">Overloaded</p>
|
||||
<p className="text-[15px] text-[var(--muted-foreground)]">Overloaded</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ function ChangePasswordForm() {
|
|||
onChange={(e) => setNewPassword(e.target.value)}
|
||||
className="h-9 text-sm"
|
||||
/>
|
||||
<p className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<p className="text-[15px] text-[var(--muted-foreground)]">
|
||||
Minimum 10 characters.
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -81,10 +81,10 @@ export default function ForgotPasswordPage() {
|
|||
</p>
|
||||
{devResetUrl && (
|
||||
<div className="rounded-md border border-[var(--border)] bg-[var(--muted)]/40 p-3">
|
||||
<p className="text-[10px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
<p className="text-[15px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
Dev mode — copy this link
|
||||
</p>
|
||||
<p className="mt-1 break-all font-mono text-[11px]">
|
||||
<p className="mt-1 break-all font-mono text-[17px]">
|
||||
{devResetUrl}
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -92,7 +92,7 @@ export default function ForgotPasswordPage() {
|
|||
</div>
|
||||
)}
|
||||
|
||||
<div className="mt-8 text-[11px] text-[var(--muted-foreground)]">
|
||||
<div className="mt-8 text-[17px] text-[var(--muted-foreground)]">
|
||||
<Link href="/login" className="underline hover:text-[var(--foreground)]">
|
||||
← Back to sign in
|
||||
</Link>
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ export function CredentialsLogin() {
|
|||
</Label>
|
||||
<Link
|
||||
href="/forgot-password"
|
||||
className="text-[10px] text-[var(--muted-foreground)] hover:text-[var(--foreground)]"
|
||||
className="text-[15px] text-[var(--muted-foreground)] hover:text-[var(--foreground)]"
|
||||
>
|
||||
Forgot?
|
||||
</Link>
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ export function MsalLogin({ config }: { config: MsalLoginConfig }) {
|
|||
<Button
|
||||
variant="outline"
|
||||
disabled
|
||||
className="w-full h-11 rounded-xl border-[var(--border)] text-[11px] font-semibold tracking-[0.06em] uppercase shadow-[var(--shadow-sm)]"
|
||||
className="w-full h-11 rounded-xl border-[var(--border)] text-[17px] font-semibold tracking-[0.06em] uppercase shadow-[var(--shadow-sm)]"
|
||||
>
|
||||
<MicrosoftIcon />
|
||||
Signing in…
|
||||
|
|
@ -130,7 +130,7 @@ export function MsalLogin({ config }: { config: MsalLoginConfig }) {
|
|||
<Button
|
||||
variant="outline"
|
||||
onClick={() => { setErrorMsg(null); setState("idle"); }}
|
||||
className="w-full h-11 rounded-xl border-[var(--border)] text-[11px] font-semibold tracking-[0.06em] uppercase shadow-[var(--shadow-sm)]"
|
||||
className="w-full h-11 rounded-xl border-[var(--border)] text-[17px] font-semibold tracking-[0.06em] uppercase shadow-[var(--shadow-sm)]"
|
||||
>
|
||||
Try again
|
||||
</Button>
|
||||
|
|
@ -142,7 +142,7 @@ export function MsalLogin({ config }: { config: MsalLoginConfig }) {
|
|||
<Button
|
||||
variant="outline"
|
||||
onClick={handleLogin}
|
||||
className="w-full h-11 rounded-xl border-[var(--border)] text-[11px] font-semibold tracking-[0.06em] uppercase hover:bg-[var(--foreground)] hover:text-[var(--background)] transition-all shadow-[var(--shadow-sm)] hover:shadow-[var(--shadow-md)]"
|
||||
className="w-full h-11 rounded-xl border-[var(--border)] text-[17px] font-semibold tracking-[0.06em] uppercase hover:bg-[var(--foreground)] hover:text-[var(--background)] transition-all shadow-[var(--shadow-sm)] hover:shadow-[var(--shadow-md)]"
|
||||
>
|
||||
<MicrosoftIcon />
|
||||
Continue with Microsoft
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ export default async function LoginPage() {
|
|||
{/* Left panel — green brand block */}
|
||||
<div className="hidden w-[40%] flex-col justify-between bg-[var(--primary)] p-10 md:flex">
|
||||
<div>
|
||||
<p className="text-[9px] font-bold tracking-[0.2em] uppercase text-[var(--primary-foreground)]/60">
|
||||
<p className="text-[14px] font-bold tracking-[0.2em] uppercase text-[var(--primary-foreground)]/60">
|
||||
Oliver Agency
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -35,12 +35,12 @@ export default async function LoginPage() {
|
|||
<h1 className="font-heading text-4xl font-black leading-[1.05] tracking-[-0.03em] text-[var(--primary-foreground)]">
|
||||
Dow Jones<br />Studio<br />Tracker
|
||||
</h1>
|
||||
<p className="mt-4 text-[11px] font-medium tracking-[0.06em] uppercase text-[var(--primary-foreground)]/60">
|
||||
<p className="mt-4 text-[17px] font-medium tracking-[0.06em] uppercase text-[var(--primary-foreground)]/60">
|
||||
Production pipeline for Dow Jones studio
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-[9px] font-semibold tracking-[0.15em] uppercase text-[var(--primary-foreground)]/40">
|
||||
<p className="text-[14px] font-semibold tracking-[0.15em] uppercase text-[var(--primary-foreground)]/40">
|
||||
Brandtech Group
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -54,7 +54,7 @@ export default async function LoginPage() {
|
|||
<h1 className="font-heading text-2xl font-black tracking-[-0.02em]">
|
||||
Dow Jones Studio Tracker
|
||||
</h1>
|
||||
<p className="mt-1 text-[10px] font-semibold tracking-[0.1em] uppercase text-[var(--muted-foreground)]">
|
||||
<p className="mt-1 text-[15px] font-semibold tracking-[0.1em] uppercase text-[var(--muted-foreground)]">
|
||||
Oliver Agency
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -74,7 +74,7 @@ export default async function LoginPage() {
|
|||
<>
|
||||
<div className="my-6 flex items-center gap-3">
|
||||
<div className="h-px flex-1 bg-[var(--border)]" />
|
||||
<span className="text-[10px] font-semibold uppercase tracking-[0.1em] text-[var(--muted-foreground)]">
|
||||
<span className="text-[15px] font-semibold uppercase tracking-[0.1em] text-[var(--muted-foreground)]">
|
||||
or
|
||||
</span>
|
||||
<div className="h-px flex-1 bg-[var(--border)]" />
|
||||
|
|
@ -88,7 +88,7 @@ export default async function LoginPage() {
|
|||
)}
|
||||
|
||||
<div className="mt-10 border-t pt-6">
|
||||
<p className="text-[9px] font-semibold tracking-[0.12em] uppercase text-[var(--muted-foreground)]/60">
|
||||
<p className="text-[14px] font-semibold tracking-[0.12em] uppercase text-[var(--muted-foreground)]/60">
|
||||
© {new Date().getFullYear()} Oliver Agency · Brandtech Group
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ export default async function PendingPage() {
|
|||
{/* Left panel — green brand block */}
|
||||
<div className="hidden w-[40%] flex-col justify-between bg-[var(--primary)] p-10 md:flex">
|
||||
<div>
|
||||
<p className="text-[9px] font-bold tracking-[0.2em] uppercase text-[var(--primary-foreground)]/60">
|
||||
<p className="text-[14px] font-bold tracking-[0.2em] uppercase text-[var(--primary-foreground)]/60">
|
||||
Oliver Agency
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -33,12 +33,12 @@ export default async function PendingPage() {
|
|||
<h1 className="font-heading text-4xl font-black leading-[1.05] tracking-[-0.03em] text-[var(--primary-foreground)]">
|
||||
HP CG<br />Production<br />Tracker
|
||||
</h1>
|
||||
<p className="mt-4 text-[11px] font-medium tracking-[0.06em] uppercase text-[var(--primary-foreground)]/60">
|
||||
<p className="mt-4 text-[17px] font-medium tracking-[0.06em] uppercase text-[var(--primary-foreground)]/60">
|
||||
Pipeline management for CG production
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-[9px] font-semibold tracking-[0.15em] uppercase text-[var(--primary-foreground)]/40">
|
||||
<p className="text-[14px] font-semibold tracking-[0.15em] uppercase text-[var(--primary-foreground)]/40">
|
||||
Brandtech Group
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -52,7 +52,7 @@ export default async function PendingPage() {
|
|||
<h1 className="font-heading text-2xl font-black tracking-[-0.02em]">
|
||||
Dow Jones Studio Tracker
|
||||
</h1>
|
||||
<p className="mt-1 text-[10px] font-semibold tracking-[0.1em] uppercase text-[var(--muted-foreground)]">
|
||||
<p className="mt-1 text-[15px] font-semibold tracking-[0.1em] uppercase text-[var(--muted-foreground)]">
|
||||
Oliver Agency
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -75,7 +75,7 @@ export default async function PendingPage() {
|
|||
</p>
|
||||
|
||||
<div className="mt-3 rounded-lg border border-[var(--border)] bg-[var(--card)] p-3">
|
||||
<p className="text-[10px] font-semibold tracking-[0.08em] uppercase text-[var(--muted-foreground)]">
|
||||
<p className="text-[15px] font-semibold tracking-[0.08em] uppercase text-[var(--muted-foreground)]">
|
||||
Signed in as
|
||||
</p>
|
||||
<p className="mt-1 text-sm font-medium">{session.user.email}</p>
|
||||
|
|
@ -91,14 +91,14 @@ export default async function PendingPage() {
|
|||
<Button
|
||||
type="submit"
|
||||
variant="outline"
|
||||
className="h-10 rounded-xl border-[var(--border)] text-[11px] font-semibold tracking-[0.06em] uppercase"
|
||||
className="h-10 rounded-xl border-[var(--border)] text-[17px] font-semibold tracking-[0.06em] uppercase"
|
||||
>
|
||||
Sign out
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
<div className="mt-10 border-t pt-6">
|
||||
<p className="text-[9px] font-semibold tracking-[0.12em] uppercase text-[var(--muted-foreground)]/60">
|
||||
<p className="text-[14px] font-semibold tracking-[0.12em] uppercase text-[var(--muted-foreground)]/60">
|
||||
© {new Date().getFullYear()} Oliver Agency · Brandtech Group
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ export default function ResetPasswordPage({ params }: Props) {
|
|||
onChange={(e) => setNewPassword(e.target.value)}
|
||||
className="h-9 text-sm"
|
||||
/>
|
||||
<p className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<p className="text-[15px] text-[var(--muted-foreground)]">
|
||||
Minimum 10 characters.
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -110,7 +110,7 @@ export default function ResetPasswordPage({ params }: Props) {
|
|||
</Button>
|
||||
</form>
|
||||
|
||||
<div className="mt-8 text-[11px] text-[var(--muted-foreground)]">
|
||||
<div className="mt-8 text-[17px] text-[var(--muted-foreground)]">
|
||||
<Link href="/login" className="underline hover:text-[var(--foreground)]">
|
||||
← Back to sign in
|
||||
</Link>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,35 @@
|
|||
--font-sans: "Public Sans", ui-sans-serif, system-ui, sans-serif;
|
||||
--font-mono: "JetBrains Mono", ui-monospace, monospace;
|
||||
|
||||
/*
|
||||
* Text scale — every named size (text-xs / text-sm / text-base …)
|
||||
* is overridden 1.5× from the Tailwind v4 defaults. Producers were
|
||||
* consistently reading the app at a squint; this pushes everything
|
||||
* up without having to edit each utility class on the page.
|
||||
*
|
||||
* Arbitrary sizes like `text-[15px]` bypass this scale entirely
|
||||
* and are handled by a separate sweep in the source files.
|
||||
*/
|
||||
--text-xs: 1.125rem; /* 18px (was 12) */
|
||||
--text-xs--line-height: 1.5rem;
|
||||
--text-sm: 1.3125rem; /* 21px (was 14) */
|
||||
--text-sm--line-height: 1.875rem;
|
||||
--text-base: 1.5rem; /* 24px (was 16) */
|
||||
--text-base--line-height: 2.25rem;
|
||||
--text-lg: 1.6875rem; /* 27px (was 18) */
|
||||
--text-lg--line-height: 2.625rem;
|
||||
--text-xl: 1.875rem; /* 30px (was 20) */
|
||||
--text-xl--line-height: 2.625rem;
|
||||
--text-2xl: 2.25rem; /* 36px (was 24) */
|
||||
--text-2xl--line-height: 3rem;
|
||||
--text-3xl: 2.8125rem; /* 45px (was 30) */
|
||||
--text-3xl--line-height: 3.375rem;
|
||||
--text-4xl: 3.375rem; /* 54px (was 36) */
|
||||
--text-4xl--line-height: 3.75rem;
|
||||
--text-5xl: 4.5rem; /* 72px (was 48) */
|
||||
--text-6xl: 5.625rem; /* 90px (was 60) */
|
||||
--text-7xl: 6.75rem; /* 108px (was 72) */
|
||||
|
||||
/* Border radius — modern soft corners, still geometric/professional */
|
||||
--radius-sm: 6px;
|
||||
--radius-md: 8px;
|
||||
|
|
@ -189,7 +218,7 @@
|
|||
* uppercase + wide tracking for labels, nav, section headers
|
||||
*/
|
||||
.label-upper {
|
||||
font-size: 10px;
|
||||
font-size: 15px; /* was 10px — scaled 1.5× with the rest of the type system */
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.1em;
|
||||
text-transform: uppercase;
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ export function AssignArtistPopover({
|
|||
<Badge
|
||||
key={a.id}
|
||||
variant="secondary"
|
||||
className="gap-1 pr-1 text-[10px]"
|
||||
className="gap-1 pr-1 text-[15px]"
|
||||
>
|
||||
{label}
|
||||
{a.role && (
|
||||
|
|
@ -121,7 +121,7 @@ export function AssignArtistPopover({
|
|||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
className="h-6 gap-1 px-1.5 text-[10px] text-[var(--muted-foreground)] hover:text-[var(--foreground)]"
|
||||
className="h-6 gap-1 px-1.5 text-[15px] text-[var(--muted-foreground)] hover:text-[var(--foreground)]"
|
||||
>
|
||||
<UserPlus className="h-3 w-3" />
|
||||
Assign
|
||||
|
|
@ -178,7 +178,7 @@ export function AssignArtistPopover({
|
|||
<div>
|
||||
<p className="font-medium">{u.name ?? u.email}</p>
|
||||
{u.name && (
|
||||
<p className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<p className="text-[15px] text-[var(--muted-foreground)]">
|
||||
{u.email}
|
||||
</p>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ export function SkillMatchSuggestions({
|
|||
{/* Avatar + Info */}
|
||||
<Avatar className="h-7 w-7 shrink-0">
|
||||
{s.userImage && <AvatarImage src={s.userImage} />}
|
||||
<AvatarFallback className="text-[9px] font-semibold">
|
||||
<AvatarFallback className="text-[14px] font-semibold">
|
||||
{(s.userName || "?")
|
||||
.split(" ")
|
||||
.map((n: string) => n[0])
|
||||
|
|
@ -103,7 +103,7 @@ export function SkillMatchSuggestions({
|
|||
{s.userName}
|
||||
</span>
|
||||
{isAssigned && (
|
||||
<Badge variant="outline" className="text-[8px] h-3.5 px-1 text-emerald-600">
|
||||
<Badge variant="outline" className="text-[12px] h-3.5 px-1 text-emerald-600">
|
||||
Assigned
|
||||
</Badge>
|
||||
)}
|
||||
|
|
@ -114,10 +114,10 @@ export function SkillMatchSuggestions({
|
|||
{s.matchedSkills?.map((ms: any) => (
|
||||
<Tooltip key={ms.skillName} delayDuration={100}>
|
||||
<TooltipTrigger asChild>
|
||||
<span className="inline-flex items-center gap-0.5 text-[9px] text-emerald-600 dark:text-emerald-400">
|
||||
<span className="inline-flex items-center gap-0.5 text-[14px] text-emerald-600 dark:text-emerald-400">
|
||||
<CheckCircle2 className="h-2.5 w-2.5" />
|
||||
{ms.skillName}
|
||||
<span className="text-[8px] text-[var(--muted-foreground)]">
|
||||
<span className="text-[12px] text-[var(--muted-foreground)]">
|
||||
({SKILL_LEVEL_LABELS[ms.userLevel] || ms.userLevel})
|
||||
</span>
|
||||
</span>
|
||||
|
|
@ -130,7 +130,7 @@ export function SkillMatchSuggestions({
|
|||
{s.missingSkills?.map((ms: any) => (
|
||||
<span
|
||||
key={ms.skillName}
|
||||
className="inline-flex items-center gap-0.5 text-[9px] text-[var(--muted-foreground)]"
|
||||
className="inline-flex items-center gap-0.5 text-[14px] text-[var(--muted-foreground)]"
|
||||
>
|
||||
<Minus className="h-2.5 w-2.5" />
|
||||
{ms.skillName}
|
||||
|
|
@ -147,7 +147,7 @@ export function SkillMatchSuggestions({
|
|||
<div className="flex flex-col items-center">
|
||||
<span
|
||||
className={cn(
|
||||
"text-[10px] font-bold",
|
||||
"text-[15px] font-bold",
|
||||
s.utilizationPercent > 100
|
||||
? "text-red-500"
|
||||
: s.utilizationPercent > 75
|
||||
|
|
@ -157,7 +157,7 @@ export function SkillMatchSuggestions({
|
|||
>
|
||||
{s.activeAssignments}/{s.maxCapacity}
|
||||
</span>
|
||||
<span className="text-[8px] text-[var(--muted-foreground)]">
|
||||
<span className="text-[12px] text-[var(--muted-foreground)]">
|
||||
load
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -172,7 +172,7 @@ export function SkillMatchSuggestions({
|
|||
<TooltipTrigger asChild>
|
||||
<div
|
||||
className={cn(
|
||||
"flex h-7 w-7 items-center justify-center rounded-full text-[10px] font-bold",
|
||||
"flex h-7 w-7 items-center justify-center rounded-full text-[15px] font-bold",
|
||||
s.overallScore >= 70
|
||||
? "bg-emerald-500/15 text-emerald-600 dark:text-emerald-400"
|
||||
: s.overallScore >= 40
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ export function CalendarDayDetail({
|
|||
<h3 className="text-sm font-bold tracking-tight">
|
||||
{format(date, "EEEE, MMMM d")}
|
||||
</h3>
|
||||
<p className="text-[10px] font-semibold tracking-[0.1em] text-muted-foreground/70 uppercase mt-0.5">
|
||||
<p className="text-[15px] font-semibold tracking-[0.1em] text-muted-foreground/70 uppercase mt-0.5">
|
||||
{events.length} {events.length === 1 ? "stage" : "stages"} active
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ export function CalendarEventPill({
|
|||
<Link
|
||||
href={`/projects/${event.deliverable.project.id}/deliverables/${event.deliverable.id}`}
|
||||
className={cn(
|
||||
"group flex items-center gap-1.5 text-[11px] leading-tight",
|
||||
"group flex items-center gap-1.5 text-[17px] leading-tight",
|
||||
"pl-0 pr-1 py-[3px] rounded-sm",
|
||||
"hover:bg-accent/60 transition-colors cursor-pointer",
|
||||
"truncate"
|
||||
|
|
@ -71,7 +71,7 @@ export function CalendarEventPill({
|
|||
<span className="font-semibold text-sm truncate">
|
||||
{event.deliverable.project.projectCode}
|
||||
</span>
|
||||
<StageStatusBadge status={event.status} className="text-[10px] shrink-0" />
|
||||
<StageStatusBadge status={event.status} className="text-[15px] shrink-0" />
|
||||
</div>
|
||||
<div className="text-xs text-muted-foreground mt-0.5 truncate">
|
||||
{event.deliverable.project.name}
|
||||
|
|
@ -136,12 +136,12 @@ export function CalendarEventPill({
|
|||
<span className="text-xs font-bold tracking-wide text-foreground">
|
||||
{event.deliverable.project.projectCode}
|
||||
</span>
|
||||
<StageStatusBadge status={event.status} className="text-[10px]" />
|
||||
<StageStatusBadge status={event.status} className="text-[15px]" />
|
||||
</div>
|
||||
<div className="text-xs text-muted-foreground mt-0.5 truncate">
|
||||
{event.deliverable.name}
|
||||
</div>
|
||||
<div className="text-[10px] text-muted-foreground/70 mt-0.5">
|
||||
<div className="text-[15px] text-muted-foreground/70 mt-0.5">
|
||||
{event.stageDefinition?.name ?? event.template.name}
|
||||
{event.assignments.length > 0 &&
|
||||
` \u00b7 ${event.assignments.map((a) => a.user.name).join(", ")}`}
|
||||
|
|
|
|||
|
|
@ -295,7 +295,7 @@ export function CalendarGrid({
|
|||
{["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"].map((day) => (
|
||||
<div
|
||||
key={day}
|
||||
className="py-2.5 text-center text-[10px] font-semibold tracking-[0.1em] text-muted-foreground/70"
|
||||
className="py-2.5 text-center text-[15px] font-semibold tracking-[0.1em] text-muted-foreground/70"
|
||||
>
|
||||
{day}
|
||||
</div>
|
||||
|
|
@ -360,7 +360,7 @@ export function CalendarGrid({
|
|||
{dayEvents.length > 0 && isCurrentMonth && (
|
||||
<span
|
||||
className={cn(
|
||||
"text-[9px] font-bold tracking-wider tabular-nums rounded-full px-1.5 py-0.5 leading-none",
|
||||
"text-[14px] font-bold tracking-wider tabular-nums rounded-full px-1.5 py-0.5 leading-none",
|
||||
dayEvents.length <= 3 &&
|
||||
"text-muted-foreground/60",
|
||||
dayEvents.length > 3 &&
|
||||
|
|
@ -381,7 +381,7 @@ export function CalendarGrid({
|
|||
<CalendarEventPill key={event.id} event={event} />
|
||||
))}
|
||||
{overflow > 0 && (
|
||||
<div className="text-[10px] font-semibold text-muted-foreground/60 pl-[11px] py-0.5 tracking-wide">
|
||||
<div className="text-[15px] font-semibold text-muted-foreground/60 pl-[11px] py-0.5 tracking-wide">
|
||||
+{overflow} more
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -468,7 +468,7 @@ export function CalendarGrid({
|
|||
{dayEvents.length > 0 && isCurrentMonth && (
|
||||
<span
|
||||
className={cn(
|
||||
"text-[9px] font-bold tracking-wider tabular-nums rounded-full px-1.5 py-0.5 leading-none",
|
||||
"text-[14px] font-bold tracking-wider tabular-nums rounded-full px-1.5 py-0.5 leading-none",
|
||||
dayEvents.length <= 3 &&
|
||||
"text-muted-foreground/60",
|
||||
dayEvents.length > 3 &&
|
||||
|
|
@ -530,7 +530,7 @@ export function CalendarGrid({
|
|||
href={`/projects/${seg.event.deliverable.project.id}/deliverables/${seg.event.deliverable.id}`}
|
||||
className={cn(
|
||||
"absolute flex items-center gap-1.5 px-2 h-[18px]",
|
||||
"text-[10px] font-semibold leading-none truncate",
|
||||
"text-[15px] font-semibold leading-none truncate",
|
||||
"transition-all duration-150",
|
||||
"hover:brightness-110 hover:shadow-sm",
|
||||
"focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary/60",
|
||||
|
|
@ -578,7 +578,7 @@ export function CalendarGrid({
|
|||
</span>
|
||||
<StageStatusBadge
|
||||
status={seg.event.status}
|
||||
className="text-[10px] shrink-0"
|
||||
className="text-[15px] shrink-0"
|
||||
/>
|
||||
</div>
|
||||
<div className="text-xs text-muted-foreground mt-0.5 truncate">
|
||||
|
|
@ -647,7 +647,7 @@ export function CalendarGrid({
|
|||
{/* Overflow indicator */}
|
||||
{overflowCount > 0 && (
|
||||
<div
|
||||
className="absolute left-2 text-[9px] font-semibold text-muted-foreground/60 tracking-wide"
|
||||
className="absolute left-2 text-[14px] font-semibold text-muted-foreground/60 tracking-wide"
|
||||
style={{ top: MAX_VISIBLE_LANES * 22 + 3 }}
|
||||
>
|
||||
+{overflowCount} more
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ export function CalendarHeatmapToggle({
|
|||
/>
|
||||
<label
|
||||
htmlFor="heatmap-toggle"
|
||||
className="text-[10px] font-semibold tracking-[0.1em] uppercase text-muted-foreground/70 cursor-pointer select-none"
|
||||
className="text-[15px] font-semibold tracking-[0.1em] uppercase text-muted-foreground/70 cursor-pointer select-none"
|
||||
>
|
||||
Heatmap
|
||||
</label>
|
||||
|
|
@ -142,7 +142,7 @@ export function CalendarHeatmapBar({
|
|||
<div className="pb-2 pt-1">
|
||||
{/* Legend labels */}
|
||||
<div className="flex items-center justify-between mb-1.5 px-0.5">
|
||||
<span className="text-[9px] font-semibold tracking-[0.1em] uppercase text-muted-foreground/50">
|
||||
<span className="text-[14px] font-semibold tracking-[0.1em] uppercase text-muted-foreground/50">
|
||||
{format(currentDate, "MMM")} Workload
|
||||
</span>
|
||||
<div className="flex items-center gap-2">
|
||||
|
|
@ -151,7 +151,7 @@ export function CalendarHeatmapBar({
|
|||
className="w-2 h-2 rounded-sm"
|
||||
style={{ backgroundColor: heatColor(0.1) }}
|
||||
/>
|
||||
<span className="text-[8px] text-muted-foreground/40 font-medium">
|
||||
<span className="text-[12px] text-muted-foreground/40 font-medium">
|
||||
Light
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -160,7 +160,7 @@ export function CalendarHeatmapBar({
|
|||
className="w-2 h-2 rounded-sm"
|
||||
style={{ backgroundColor: heatColor(0.5) }}
|
||||
/>
|
||||
<span className="text-[8px] text-muted-foreground/40 font-medium">
|
||||
<span className="text-[12px] text-muted-foreground/40 font-medium">
|
||||
Moderate
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -169,7 +169,7 @@ export function CalendarHeatmapBar({
|
|||
className="w-2 h-2 rounded-sm"
|
||||
style={{ backgroundColor: heatColor(1) }}
|
||||
/>
|
||||
<span className="text-[8px] text-muted-foreground/40 font-medium">
|
||||
<span className="text-[12px] text-muted-foreground/40 font-medium">
|
||||
Heavy
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -205,7 +205,7 @@ export function CalendarHeatmapBar({
|
|||
>
|
||||
<span
|
||||
className={cn(
|
||||
"text-[8px] font-bold leading-none tabular-nums",
|
||||
"text-[12px] font-bold leading-none tabular-nums",
|
||||
ratio > 0.5
|
||||
? "text-white/90"
|
||||
: ratio > 0
|
||||
|
|
@ -218,7 +218,7 @@ export function CalendarHeatmapBar({
|
|||
{count > 0 && (
|
||||
<span
|
||||
className={cn(
|
||||
"text-[7px] font-bold leading-none mt-0.5 tabular-nums",
|
||||
"text-[11px] font-bold leading-none mt-0.5 tabular-nums",
|
||||
ratio > 0.5
|
||||
? "text-white/70"
|
||||
: "text-foreground/50"
|
||||
|
|
@ -233,7 +233,7 @@ export function CalendarHeatmapBar({
|
|||
className={cn(
|
||||
"absolute -bottom-8 left-1/2 -translate-x-1/2 z-50",
|
||||
"px-1.5 py-0.5 rounded bg-foreground text-background",
|
||||
"text-[9px] font-medium whitespace-nowrap",
|
||||
"text-[14px] font-medium whitespace-nowrap",
|
||||
"opacity-0 scale-95 group-hover:opacity-100 group-hover:scale-100",
|
||||
"transition-all duration-150 pointer-events-none"
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ export function CalendarView() {
|
|||
<div className="flex items-center gap-3">
|
||||
{!isLoading && events.length > 0 && (
|
||||
<>
|
||||
<span className="text-[10px] font-semibold tracking-[0.1em] text-muted-foreground/70 uppercase tabular-nums">
|
||||
<span className="text-[15px] font-semibold tracking-[0.1em] text-muted-foreground/70 uppercase tabular-nums">
|
||||
{events.length} stages
|
||||
</span>
|
||||
<div className="w-px h-3.5 bg-border/40" />
|
||||
|
|
@ -123,7 +123,7 @@ export function CalendarView() {
|
|||
type="button"
|
||||
onClick={() => setViewMode("bars")}
|
||||
className={cn(
|
||||
"flex items-center gap-1.5 px-2.5 py-1 rounded-md text-[10px] font-semibold tracking-[0.08em] uppercase transition-all duration-150",
|
||||
"flex items-center gap-1.5 px-2.5 py-1 rounded-md text-[15px] font-semibold tracking-[0.08em] uppercase transition-all duration-150",
|
||||
"focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary/40",
|
||||
viewMode === "bars"
|
||||
? "bg-background text-foreground shadow-sm"
|
||||
|
|
@ -139,7 +139,7 @@ export function CalendarView() {
|
|||
type="button"
|
||||
onClick={() => setViewMode("pills")}
|
||||
className={cn(
|
||||
"flex items-center gap-1.5 px-2.5 py-1 rounded-md text-[10px] font-semibold tracking-[0.08em] uppercase transition-all duration-150",
|
||||
"flex items-center gap-1.5 px-2.5 py-1 rounded-md text-[15px] font-semibold tracking-[0.08em] uppercase transition-all duration-150",
|
||||
"focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary/40",
|
||||
viewMode === "pills"
|
||||
? "bg-background text-foreground shadow-sm"
|
||||
|
|
@ -201,7 +201,7 @@ function StatusLegend() {
|
|||
className="w-2 h-2 rounded-full shrink-0"
|
||||
style={{ backgroundColor: `var(${item.var})` }}
|
||||
/>
|
||||
<span className="text-[9px] text-muted-foreground/60 font-medium">
|
||||
<span className="text-[14px] text-muted-foreground/60 font-medium">
|
||||
{item.label}
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ function ProviderBadge({ provider }: { provider: "claude" | "ollama" | "none" })
|
|||
|
||||
if (provider === "ollama") {
|
||||
return (
|
||||
<div className="flex items-center gap-1 rounded-full bg-orange-100 px-2 py-0.5 text-[10px] font-medium text-orange-700 dark:bg-orange-900/50 dark:text-orange-300">
|
||||
<div className="flex items-center gap-1 rounded-full bg-orange-100 px-2 py-0.5 text-[15px] font-medium text-orange-700 dark:bg-orange-900/50 dark:text-orange-300">
|
||||
<Zap className="h-2.5 w-2.5" />
|
||||
Ollama
|
||||
</div>
|
||||
|
|
@ -83,7 +83,7 @@ function ProviderBadge({ provider }: { provider: "claude" | "ollama" | "none" })
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-1 rounded-full bg-purple-100 px-2 py-0.5 text-[10px] font-medium text-purple-700 dark:bg-purple-900/50 dark:text-purple-300">
|
||||
<div className="flex items-center gap-1 rounded-full bg-purple-100 px-2 py-0.5 text-[15px] font-medium text-purple-700 dark:bg-purple-900/50 dark:text-purple-300">
|
||||
<Zap className="h-2.5 w-2.5" />
|
||||
Claude
|
||||
</div>
|
||||
|
|
@ -98,7 +98,7 @@ function ToolCallIndicator({ toolCalls }: { toolCalls: ToolStatus[] }) {
|
|||
{toolCalls.map((tc) => (
|
||||
<div
|
||||
key={tc.toolCallId}
|
||||
className="flex items-center gap-1.5 text-[11px] text-[var(--muted-foreground)]"
|
||||
className="flex items-center gap-1.5 text-[17px] text-[var(--muted-foreground)]"
|
||||
>
|
||||
{tc.status === "running" ? (
|
||||
<Loader2 className="h-3 w-3 animate-spin text-blue-500" />
|
||||
|
|
@ -168,10 +168,10 @@ function MutationConfirmCard({
|
|||
</p>
|
||||
{/* Show input details for transparency */}
|
||||
<details className="mt-1.5">
|
||||
<summary className="cursor-pointer text-[10px] text-amber-600 dark:text-amber-400 hover:underline">
|
||||
<summary className="cursor-pointer text-[15px] text-amber-600 dark:text-amber-400 hover:underline">
|
||||
Details
|
||||
</summary>
|
||||
<pre className="mt-1 max-h-24 overflow-auto rounded bg-amber-100 p-1.5 text-[10px] text-amber-900 dark:bg-amber-900/40 dark:text-amber-100">
|
||||
<pre className="mt-1 max-h-24 overflow-auto rounded bg-amber-100 p-1.5 text-[15px] text-amber-900 dark:bg-amber-900/40 dark:text-amber-100">
|
||||
{JSON.stringify(pending.input, null, 2)}
|
||||
</pre>
|
||||
</details>
|
||||
|
|
@ -233,7 +233,7 @@ function SuggestionChips({
|
|||
>
|
||||
<span
|
||||
className={cn(
|
||||
"flex h-4 w-4 shrink-0 items-center justify-center rounded text-[9px] font-bold",
|
||||
"flex h-4 w-4 shrink-0 items-center justify-center rounded text-[14px] font-bold",
|
||||
s.type === "project" && "bg-blue-100 text-blue-700 dark:bg-blue-900/50 dark:text-blue-300",
|
||||
s.type === "deliverable" && "bg-green-100 text-green-700 dark:bg-green-900/50 dark:text-green-300",
|
||||
s.type === "user" && "bg-purple-100 text-purple-700 dark:bg-purple-900/50 dark:text-purple-300",
|
||||
|
|
@ -245,7 +245,7 @@ function SuggestionChips({
|
|||
<span className="flex flex-col leading-tight">
|
||||
<span className="font-medium">{s.label}</span>
|
||||
{s.description && (
|
||||
<span className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<span className="text-[15px] text-[var(--muted-foreground)]">
|
||||
{s.description}
|
||||
</span>
|
||||
)}
|
||||
|
|
@ -304,12 +304,12 @@ function EntityResultCard({
|
|||
<p className="text-xs font-medium truncate">{entity.name}</p>
|
||||
<div className="flex items-center gap-1.5 mt-0.5">
|
||||
{entity.code && (
|
||||
<span className="font-mono text-[9px] text-[var(--muted-foreground)]">
|
||||
<span className="font-mono text-[14px] text-[var(--muted-foreground)]">
|
||||
{entity.code}
|
||||
</span>
|
||||
)}
|
||||
{entity.projectName && !isProject && (
|
||||
<span className="text-[9px] text-[var(--muted-foreground)] truncate">
|
||||
<span className="text-[14px] text-[var(--muted-foreground)] truncate">
|
||||
in {entity.projectName}
|
||||
</span>
|
||||
)}
|
||||
|
|
@ -320,7 +320,7 @@ function EntityResultCard({
|
|||
{entity.status && !isUser && (
|
||||
<Badge
|
||||
className={cn(
|
||||
"text-[8px] px-1.5 py-0 h-3.5 font-semibold",
|
||||
"text-[12px] px-1.5 py-0 h-3.5 font-semibold",
|
||||
statusColor(entity.status)
|
||||
)}
|
||||
>
|
||||
|
|
@ -332,16 +332,16 @@ function EntityResultCard({
|
|||
</div>
|
||||
|
||||
<div className="mt-1.5 flex items-center gap-2">
|
||||
<span className="label-upper !text-[8px]">
|
||||
<span className="label-upper !text-[12px]">
|
||||
{isProject ? "Project" : isUser ? "Artist" : "Deliverable"}
|
||||
</span>
|
||||
{isUser && entity.status && (
|
||||
<span className="text-[9px] text-[var(--muted-foreground)]">
|
||||
<span className="text-[14px] text-[var(--muted-foreground)]">
|
||||
{entity.status}
|
||||
</span>
|
||||
)}
|
||||
{entity.priority && !isUser && (
|
||||
<Badge variant="outline" className="text-[7px] px-1 py-0 h-3">
|
||||
<Badge variant="outline" className="text-[11px] px-1 py-0 h-3">
|
||||
{entity.priority}
|
||||
</Badge>
|
||||
)}
|
||||
|
|
@ -455,7 +455,7 @@ function MessageBubble({
|
|||
{message.isLoading && message.toolCalls && message.toolCalls.length > 0 && !message.content && (
|
||||
<div className="mt-1.5 flex items-center gap-2">
|
||||
<Loader2 className="h-3 w-3 animate-spin" />
|
||||
<span className="text-[11px] text-[var(--muted-foreground)]">Composing response...</span>
|
||||
<span className="text-[17px] text-[var(--muted-foreground)]">Composing response...</span>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
|
|
@ -555,7 +555,7 @@ export function ChatPanel() {
|
|||
<Bot className="h-4 w-4" />
|
||||
AI Assistant
|
||||
{pageContext.activeProjectId && (
|
||||
<span className="flex items-center gap-1 rounded-full bg-blue-100 px-2 py-0.5 text-[10px] font-medium text-blue-700 dark:bg-blue-900/50 dark:text-blue-300">
|
||||
<span className="flex items-center gap-1 rounded-full bg-blue-100 px-2 py-0.5 text-[15px] font-medium text-blue-700 dark:bg-blue-900/50 dark:text-blue-300">
|
||||
<MapPin className="h-2.5 w-2.5" />
|
||||
Project context active
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ export function CommandPalette() {
|
|||
>
|
||||
<Bot className="mr-2 h-4 w-4" />
|
||||
AI Assistant
|
||||
<span className="ml-auto text-[10px] text-[var(--muted-foreground)]">
|
||||
<span className="ml-auto text-[15px] text-[var(--muted-foreground)]">
|
||||
Ask anything
|
||||
</span>
|
||||
</CommandItem>
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ function CommentItem({
|
|||
<div className="flex gap-2.5">
|
||||
<Avatar className="h-7 w-7 shrink-0">
|
||||
<AvatarImage src={comment.author?.image} />
|
||||
<AvatarFallback className="text-[10px]">
|
||||
<AvatarFallback className="text-[15px]">
|
||||
{getInitials(comment.author?.name, comment.author?.email)}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
|
|
@ -64,7 +64,7 @@ function CommentItem({
|
|||
<span className="text-xs font-medium">
|
||||
{comment.author?.name ?? comment.author?.email ?? "Unknown"}
|
||||
</span>
|
||||
<span className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<span className="text-[15px] text-[var(--muted-foreground)]">
|
||||
{formatDistanceToNow(new Date(comment.createdAt), {
|
||||
addSuffix: true,
|
||||
})}
|
||||
|
|
@ -78,7 +78,7 @@ function CommentItem({
|
|||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
className="h-6 px-1.5 text-[10px] text-[var(--muted-foreground)]"
|
||||
className="h-6 px-1.5 text-[15px] text-[var(--muted-foreground)]"
|
||||
onClick={() => setShowReply(!showReply)}
|
||||
>
|
||||
<Reply className="mr-0.5 h-3 w-3" />
|
||||
|
|
@ -88,7 +88,7 @@ function CommentItem({
|
|||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
className="h-6 px-1.5 text-[10px] text-[var(--muted-foreground)] hover:text-[var(--destructive)]"
|
||||
className="h-6 px-1.5 text-[15px] text-[var(--muted-foreground)] hover:text-[var(--destructive)]"
|
||||
onClick={() =>
|
||||
deleteComment.mutate(comment.id, {
|
||||
onError: (e) =>
|
||||
|
|
@ -111,7 +111,7 @@ function CommentItem({
|
|||
<div className="flex gap-1.5">
|
||||
<Button
|
||||
size="sm"
|
||||
className="h-6 text-[10px]"
|
||||
className="h-6 text-[15px]"
|
||||
disabled={createComment.isPending || !replyText.trim()}
|
||||
onClick={handleReply}
|
||||
>
|
||||
|
|
@ -120,7 +120,7 @@ function CommentItem({
|
|||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
className="h-6 text-[10px]"
|
||||
className="h-6 text-[15px]"
|
||||
onClick={() => setShowReply(false)}
|
||||
>
|
||||
Cancel
|
||||
|
|
|
|||
|
|
@ -149,11 +149,11 @@ export function DeliverableBoard({
|
|||
className="h-2 w-2 rounded-full"
|
||||
style={{ background: col.accent }}
|
||||
/>
|
||||
<span className="text-[11px] font-bold uppercase tracking-wider">
|
||||
<span className="text-[17px] font-bold uppercase tracking-wider">
|
||||
{col.label}
|
||||
</span>
|
||||
</div>
|
||||
<span className="rounded-full bg-[var(--card)] px-2 py-0.5 text-[10px] font-semibold tabular-nums text-[var(--muted-foreground)]">
|
||||
<span className="rounded-full bg-[var(--card)] px-2 py-0.5 text-[15px] font-semibold tabular-nums text-[var(--muted-foreground)]">
|
||||
{col.deliverables.length}
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -169,7 +169,7 @@ export function DeliverableBoard({
|
|||
)}
|
||||
>
|
||||
{col.deliverables.length === 0 && !snapshot.isDraggingOver && (
|
||||
<div className="py-6 text-center text-[10px] text-[var(--muted-foreground)]/60">
|
||||
<div className="py-6 text-center text-[15px] text-[var(--muted-foreground)]/60">
|
||||
—
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -303,7 +303,7 @@ function DeliverableCard({ deliverable: d }: { deliverable: AllDeliverableRow })
|
|||
className="block rounded-md border bg-[var(--card)] p-2.5 text-left shadow-[var(--shadow-xs)] transition-all hover:border-[var(--primary)] hover:shadow-[var(--shadow-sm)]"
|
||||
>
|
||||
{/* Top row: OMG #, priority dot, team */}
|
||||
<div className="mb-1.5 flex items-center gap-1.5 text-[10px]">
|
||||
<div className="mb-1.5 flex items-center gap-1.5 text-[15px]">
|
||||
{d.project.omgJobNumber && (
|
||||
<span className="font-mono tabular-nums text-[var(--muted-foreground)]">
|
||||
#{d.project.omgJobNumber}
|
||||
|
|
@ -316,7 +316,7 @@ function DeliverableCard({ deliverable: d }: { deliverable: AllDeliverableRow })
|
|||
{d.project.clientTeam && (
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="ml-auto h-4 px-1 text-[9px] uppercase tracking-wider"
|
||||
className="ml-auto h-4 px-1 text-[14px] uppercase tracking-wider"
|
||||
>
|
||||
{d.project.clientTeam.name}
|
||||
</Badge>
|
||||
|
|
@ -327,12 +327,12 @@ function DeliverableCard({ deliverable: d }: { deliverable: AllDeliverableRow })
|
|||
<div className="mb-1 line-clamp-2 text-xs font-semibold leading-snug">{d.name}</div>
|
||||
|
||||
{/* Project name (smaller, muted) */}
|
||||
<div className="mb-1.5 line-clamp-1 text-[10px] text-[var(--muted-foreground)]">
|
||||
<div className="mb-1.5 line-clamp-1 text-[15px] text-[var(--muted-foreground)]">
|
||||
{d.project.name}
|
||||
</div>
|
||||
|
||||
{/* Assignee + deadline */}
|
||||
<div className="flex items-center justify-between text-[10px] text-[var(--muted-foreground)]">
|
||||
<div className="flex items-center justify-between text-[15px] text-[var(--muted-foreground)]">
|
||||
<span className="truncate">
|
||||
{primary ?? <span className="italic">Unassigned</span>}
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -280,13 +280,13 @@ export function BulkImportDialog({
|
|||
<tbody>
|
||||
{preview.rows.map((row, i) => (
|
||||
<tr key={i} className="border-t hover:bg-[var(--muted)]/40">
|
||||
<td className="whitespace-nowrap px-2 py-1 font-mono text-[10px] text-[var(--muted-foreground)]">
|
||||
<td className="whitespace-nowrap px-2 py-1 font-mono text-[15px] text-[var(--muted-foreground)]">
|
||||
{row.sourceSheet === "Q1_2026" ? "Q1" : "Q2"}
|
||||
</td>
|
||||
<td className="max-w-[220px] px-2 py-1">
|
||||
<p className="truncate font-medium leading-tight">{row.name}</p>
|
||||
{row.deliverableName !== row.name && (
|
||||
<p className="truncate text-[10px] text-[var(--muted-foreground)]">
|
||||
<p className="truncate text-[15px] text-[var(--muted-foreground)]">
|
||||
↳ {row.deliverableName}
|
||||
</p>
|
||||
)}
|
||||
|
|
@ -310,7 +310,7 @@ export function BulkImportDialog({
|
|||
</td>
|
||||
<td className="px-2 py-1">
|
||||
<span
|
||||
className={`inline-block rounded-full px-2 py-0.5 text-[10px] font-medium ${STATUS_COLORS[row.deliverableStatus] ?? ""}`}
|
||||
className={`inline-block rounded-full px-2 py-0.5 text-[15px] font-medium ${STATUS_COLORS[row.deliverableStatus] ?? ""}`}
|
||||
>
|
||||
{STATUS_LABELS[row.deliverableStatus] ?? row.deliverableStatus}
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ export function Breadcrumbs() {
|
|||
<ChevronRight className="h-3 w-3 text-[var(--border)]" aria-hidden="true" />
|
||||
{crumb.isLast ? (
|
||||
<span
|
||||
className="text-[11px] font-semibold tracking-[0.04em] uppercase text-[var(--foreground)]"
|
||||
className="text-[17px] font-semibold tracking-[0.04em] uppercase text-[var(--foreground)]"
|
||||
aria-current="page"
|
||||
>
|
||||
{crumb.label}
|
||||
|
|
@ -53,7 +53,7 @@ export function Breadcrumbs() {
|
|||
) : (
|
||||
<Link
|
||||
href={crumb.href}
|
||||
className="text-[11px] font-semibold tracking-[0.04em] uppercase text-[var(--muted-foreground)] transition-colors hover:text-[var(--foreground)]"
|
||||
className="text-[17px] font-semibold tracking-[0.04em] uppercase text-[var(--muted-foreground)] transition-colors hover:text-[var(--foreground)]"
|
||||
>
|
||||
{crumb.label}
|
||||
</Link>
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ function NavLinks({
|
|||
href={item.href}
|
||||
onClick={onNavigate}
|
||||
className={cn(
|
||||
"flex items-center gap-3 rounded-lg px-3 py-2 text-[11px] font-semibold tracking-[0.08em] uppercase transition-all duration-150",
|
||||
"flex items-center gap-3 rounded-lg px-3 py-2 text-[17px] font-semibold tracking-[0.08em] uppercase transition-all duration-150",
|
||||
isActive
|
||||
? "bg-[var(--primary)] text-[var(--primary-foreground)] shadow-[var(--shadow-sm)]"
|
||||
: "text-[var(--muted-foreground)] hover:bg-[var(--background)] hover:text-[var(--foreground)]",
|
||||
|
|
@ -105,7 +105,7 @@ function NavLinks({
|
|||
href={item.href}
|
||||
onClick={onNavigate}
|
||||
className={cn(
|
||||
"flex items-center gap-3 rounded-lg px-3 py-2 text-[11px] font-semibold tracking-[0.08em] uppercase transition-all duration-150",
|
||||
"flex items-center gap-3 rounded-lg px-3 py-2 text-[17px] font-semibold tracking-[0.08em] uppercase transition-all duration-150",
|
||||
isActive
|
||||
? "bg-[var(--primary)] text-[var(--primary-foreground)] shadow-[var(--shadow-sm)]"
|
||||
: "text-[var(--muted-foreground)] hover:bg-[var(--background)] hover:text-[var(--foreground)]",
|
||||
|
|
@ -137,7 +137,7 @@ function NavLinks({
|
|||
<form action={signOutAction}>
|
||||
<button
|
||||
type="submit"
|
||||
className="flex w-full items-center justify-center rounded-lg px-2 py-2 text-[11px] font-semibold tracking-[0.08em] uppercase transition-all duration-150 text-[var(--muted-foreground)] hover:bg-[var(--background)] hover:text-destructive"
|
||||
className="flex w-full items-center justify-center rounded-lg px-2 py-2 text-[17px] font-semibold tracking-[0.08em] uppercase transition-all duration-150 text-[var(--muted-foreground)] hover:bg-[var(--background)] hover:text-destructive"
|
||||
aria-label="Sign out"
|
||||
>
|
||||
<LogOut className="h-4 w-4 shrink-0" />
|
||||
|
|
@ -150,7 +150,7 @@ function NavLinks({
|
|||
<form action={signOutAction}>
|
||||
<button
|
||||
type="submit"
|
||||
className="flex w-full items-center gap-3 rounded-lg px-3 py-2 text-[11px] font-semibold tracking-[0.08em] uppercase transition-all duration-150 text-[var(--muted-foreground)] hover:bg-[var(--background)] hover:text-destructive"
|
||||
className="flex w-full items-center gap-3 rounded-lg px-3 py-2 text-[17px] font-semibold tracking-[0.08em] uppercase transition-all duration-150 text-[var(--muted-foreground)] hover:bg-[var(--background)] hover:text-destructive"
|
||||
>
|
||||
<LogOut className="h-4 w-4 shrink-0" />
|
||||
<span>Sign out</span>
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ export function Topbar() {
|
|||
{unreadCount > 0 && (
|
||||
<Badge
|
||||
variant="destructive"
|
||||
className="absolute -right-1 -top-1 h-4 min-w-4 rounded-full p-0 text-[10px] leading-4"
|
||||
className="absolute -right-1 -top-1 h-4 min-w-4 rounded-full p-0 text-[15px] leading-4"
|
||||
aria-hidden="true"
|
||||
>
|
||||
{unreadCount > 99 ? "99+" : unreadCount}
|
||||
|
|
@ -99,14 +99,14 @@ export function Topbar() {
|
|||
</PopoverTrigger>
|
||||
<PopoverContent align="end" className="w-80 p-0 rounded-xl shadow-[var(--shadow-lg)]">
|
||||
<div className="flex items-center justify-between border-b px-3 py-2.5">
|
||||
<span className="text-[10px] font-semibold tracking-[0.1em] uppercase text-[var(--muted-foreground)]">
|
||||
<span className="text-[15px] font-semibold tracking-[0.1em] uppercase text-[var(--muted-foreground)]">
|
||||
Notifications
|
||||
</span>
|
||||
{unreadCount > 0 && (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
className="h-6 text-[10px]"
|
||||
className="h-6 text-[15px]"
|
||||
onClick={() => markAllRead.mutate()}
|
||||
disabled={markAllRead.isPending}
|
||||
>
|
||||
|
|
@ -144,10 +144,10 @@ export function Topbar() {
|
|||
) : (
|
||||
<p className="text-xs font-medium">{notif.title}</p>
|
||||
)}
|
||||
<p className="mt-0.5 truncate text-[10px] text-[var(--muted-foreground)]">
|
||||
<p className="mt-0.5 truncate text-[15px] text-[var(--muted-foreground)]">
|
||||
{notif.message}
|
||||
</p>
|
||||
<p className="mt-0.5 text-[10px] text-[var(--muted-foreground)]">
|
||||
<p className="mt-0.5 text-[15px] text-[var(--muted-foreground)]">
|
||||
{formatDistanceToNow(new Date(notif.createdAt), {
|
||||
addSuffix: true,
|
||||
})}
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ export function PipelineStageList({
|
|||
{stage.isCriticalGate && (
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="h-4 gap-0.5 border-amber-500/60 text-amber-600 dark:text-amber-400 text-[9px] px-1.5 shrink-0"
|
||||
className="h-4 gap-0.5 border-amber-500/60 text-amber-600 dark:text-amber-400 text-[14px] px-1.5 shrink-0"
|
||||
>
|
||||
<Shield className="size-2.5" />
|
||||
GATE
|
||||
|
|
@ -98,7 +98,7 @@ export function PipelineStageList({
|
|||
{stage.isOptional && (
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="h-4 text-[9px] px-1.5 shrink-0"
|
||||
className="h-4 text-[14px] px-1.5 shrink-0"
|
||||
>
|
||||
Optional
|
||||
</Badge>
|
||||
|
|
@ -107,7 +107,7 @@ export function PipelineStageList({
|
|||
|
||||
{/* Estimated days */}
|
||||
{stage.estimatedDays != null && (
|
||||
<span className="flex items-center gap-0.5 text-[10px] text-[var(--muted-foreground)] shrink-0">
|
||||
<span className="flex items-center gap-0.5 text-[15px] text-[var(--muted-foreground)] shrink-0">
|
||||
<Clock className="size-2.5" />
|
||||
{stage.estimatedDays}d
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ export function PipelineValidationBanner({
|
|||
>
|
||||
<AlertCircle className="size-4 shrink-0 mt-0.5" />
|
||||
<div className="flex flex-col gap-1 min-w-0">
|
||||
<span className="label-upper text-[10px] font-semibold">
|
||||
<span className="label-upper text-[15px] font-semibold">
|
||||
Errors
|
||||
</span>
|
||||
<ul className="list-disc list-inside text-sm space-y-0.5">
|
||||
|
|
@ -48,7 +48,7 @@ export function PipelineValidationBanner({
|
|||
>
|
||||
<AlertTriangle className="size-4 shrink-0 mt-0.5" />
|
||||
<div className="flex flex-col gap-1 min-w-0">
|
||||
<span className="label-upper text-[10px] font-semibold">
|
||||
<span className="label-upper text-[15px] font-semibold">
|
||||
Warnings
|
||||
</span>
|
||||
<ul className="list-disc list-inside text-sm space-y-0.5">
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ export function StageEditSheet({
|
|||
<div className="flex flex-col gap-5 px-4">
|
||||
{/* Name */}
|
||||
<div className="flex flex-col gap-1.5">
|
||||
<Label htmlFor="stage-name" className="label-upper text-[10px]">
|
||||
<Label htmlFor="stage-name" className="label-upper text-[15px]">
|
||||
Name
|
||||
</Label>
|
||||
<Input
|
||||
|
|
@ -141,7 +141,7 @@ export function StageEditSheet({
|
|||
|
||||
{/* Slug */}
|
||||
<div className="flex flex-col gap-1.5">
|
||||
<Label htmlFor="stage-slug" className="label-upper text-[10px]">
|
||||
<Label htmlFor="stage-slug" className="label-upper text-[15px]">
|
||||
Slug
|
||||
</Label>
|
||||
<Input
|
||||
|
|
@ -157,7 +157,7 @@ export function StageEditSheet({
|
|||
<div className="flex flex-col gap-1.5">
|
||||
<Label
|
||||
htmlFor="stage-description"
|
||||
className="label-upper text-[10px]"
|
||||
className="label-upper text-[15px]"
|
||||
>
|
||||
Description
|
||||
</Label>
|
||||
|
|
@ -172,7 +172,7 @@ export function StageEditSheet({
|
|||
|
||||
{/* Estimated Days */}
|
||||
<div className="flex flex-col gap-1.5">
|
||||
<Label htmlFor="stage-days" className="label-upper text-[10px]">
|
||||
<Label htmlFor="stage-days" className="label-upper text-[15px]">
|
||||
Estimated Days
|
||||
</Label>
|
||||
<Input
|
||||
|
|
@ -188,7 +188,7 @@ export function StageEditSheet({
|
|||
|
||||
{/* Color */}
|
||||
<div className="flex flex-col gap-1.5">
|
||||
<Label htmlFor="stage-color" className="label-upper text-[10px]">
|
||||
<Label htmlFor="stage-color" className="label-upper text-[15px]">
|
||||
Color
|
||||
</Label>
|
||||
<div className="flex items-center gap-2">
|
||||
|
|
@ -220,7 +220,7 @@ export function StageEditSheet({
|
|||
>
|
||||
Critical Gate
|
||||
</Label>
|
||||
<span className="text-[11px] text-[var(--muted-foreground)]">
|
||||
<span className="text-[17px] text-[var(--muted-foreground)]">
|
||||
Requires approval before proceeding
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -239,7 +239,7 @@ export function StageEditSheet({
|
|||
>
|
||||
Optional
|
||||
</Label>
|
||||
<span className="text-[11px] text-[var(--muted-foreground)]">
|
||||
<span className="text-[17px] text-[var(--muted-foreground)]">
|
||||
Can be skipped in the pipeline
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ export function StageNode({ data, selected }: NodeProps) {
|
|||
{isOptional && (
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="h-3.5 text-[8px] px-1 w-fit"
|
||||
className="h-3.5 text-[12px] px-1 w-fit"
|
||||
>
|
||||
Optional
|
||||
</Badge>
|
||||
|
|
|
|||
|
|
@ -143,11 +143,11 @@ export function ProjectBoard({ projects, groupBy, pipelineStages }: ProjectBoard
|
|||
className="h-2 w-2 rounded-full"
|
||||
style={{ background: col.accent }}
|
||||
/>
|
||||
<span className="text-[11px] font-bold uppercase tracking-wider">
|
||||
<span className="text-[17px] font-bold uppercase tracking-wider">
|
||||
{col.label}
|
||||
</span>
|
||||
</div>
|
||||
<span className="rounded-full bg-[var(--card)] px-2 py-0.5 text-[10px] font-semibold tabular-nums text-[var(--muted-foreground)]">
|
||||
<span className="rounded-full bg-[var(--card)] px-2 py-0.5 text-[15px] font-semibold tabular-nums text-[var(--muted-foreground)]">
|
||||
{col.projects.length}
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -165,7 +165,7 @@ export function ProjectBoard({ projects, groupBy, pipelineStages }: ProjectBoard
|
|||
)}
|
||||
>
|
||||
{col.projects.length === 0 && !snapshot.isDraggingOver && (
|
||||
<div className="py-6 text-center text-[10px] text-[var(--muted-foreground)]/60">
|
||||
<div className="py-6 text-center text-[15px] text-[var(--muted-foreground)]/60">
|
||||
—
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -190,7 +190,7 @@ export function ProjectBoard({ projects, groupBy, pipelineStages }: ProjectBoard
|
|||
) : (
|
||||
<div className="flex flex-1 flex-col gap-2 p-2">
|
||||
{col.projects.length === 0 ? (
|
||||
<div className="py-6 text-center text-[10px] text-[var(--muted-foreground)]/60">
|
||||
<div className="py-6 text-center text-[15px] text-[var(--muted-foreground)]/60">
|
||||
—
|
||||
</div>
|
||||
) : (
|
||||
|
|
@ -308,7 +308,7 @@ function ProjectCard({ project: p }: { project: BoardProject }) {
|
|||
className="block rounded-md border bg-[var(--card)] p-2.5 text-left shadow-[var(--shadow-xs)] transition-all hover:border-[var(--primary)] hover:shadow-[var(--shadow-sm)]"
|
||||
>
|
||||
{/* Top row: OMG # + priority dot + team */}
|
||||
<div className="mb-1.5 flex items-center gap-1.5 text-[10px]">
|
||||
<div className="mb-1.5 flex items-center gap-1.5 text-[15px]">
|
||||
{p.omgJobNumber && (
|
||||
<span className="font-mono tabular-nums text-[var(--muted-foreground)]">
|
||||
#{p.omgJobNumber}
|
||||
|
|
@ -321,7 +321,7 @@ function ProjectCard({ project: p }: { project: BoardProject }) {
|
|||
{p.clientTeam && (
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="ml-auto h-4 px-1 text-[9px] uppercase tracking-wider"
|
||||
className="ml-auto h-4 px-1 text-[14px] uppercase tracking-wider"
|
||||
>
|
||||
{p.clientTeam.name}
|
||||
</Badge>
|
||||
|
|
@ -332,7 +332,7 @@ function ProjectCard({ project: p }: { project: BoardProject }) {
|
|||
<div className="mb-2 line-clamp-2 text-xs font-semibold leading-snug">{p.name}</div>
|
||||
|
||||
{/* Meta — deliverables + deadline */}
|
||||
<div className="flex items-center justify-between text-[10px] text-[var(--muted-foreground)]">
|
||||
<div className="flex items-center justify-between text-[15px] text-[var(--muted-foreground)]">
|
||||
<span>
|
||||
{p._count?.deliverables ?? 0}{" "}
|
||||
deliverable{(p._count?.deliverables ?? 0) === 1 ? "" : "s"}
|
||||
|
|
@ -357,7 +357,7 @@ function ProjectCard({ project: p }: { project: BoardProject }) {
|
|||
{/* Current stage label (only meaningful in status-grouped view where
|
||||
the column doesn't already tell you the stage) */}
|
||||
{progress?.dominantStage && (
|
||||
<div className="mt-1.5 text-[10px] text-[var(--muted-foreground)]">
|
||||
<div className="mt-1.5 text-[15px] text-[var(--muted-foreground)]">
|
||||
at {progress.dominantStage.name} ({progress.dominantStage.count}/
|
||||
{progress.totalDeliverables})
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ export function ProjectFormDialog({
|
|||
placeholder="e.g. 2337959"
|
||||
{...register("omgJobNumber")}
|
||||
/>
|
||||
<p className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<p className="text-[15px] text-[var(--muted-foreground)]">
|
||||
Canonical key — matches the XLSX ingest + OMG webhook.
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -205,7 +205,7 @@ export function ProjectFormDialog({
|
|||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<p className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<p className="text-[15px] text-[var(--muted-foreground)]">
|
||||
Drives who sees this project. Required for scoped users.
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -300,7 +300,7 @@ export function ProjectFormDialog({
|
|||
</SelectContent>
|
||||
</Select>
|
||||
{hasDeliverables && (
|
||||
<p className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<p className="text-[15px] text-[var(--muted-foreground)]">
|
||||
Pipeline cannot be changed after deliverables exist.
|
||||
</p>
|
||||
)}
|
||||
|
|
@ -318,14 +318,14 @@ export function ProjectFormDialog({
|
|||
<div className="space-y-2">
|
||||
<Label htmlFor="startDate">Brief Accepted Date</Label>
|
||||
<Input id="startDate" type="date" {...register("startDate")} />
|
||||
<p className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<p className="text-[15px] text-[var(--muted-foreground)]">
|
||||
Dow XLSX: “Brief Acceptance Date”.
|
||||
</p>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="dueDate">External Deadline</Label>
|
||||
<Input id="dueDate" type="date" {...register("dueDate")} />
|
||||
<p className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<p className="text-[15px] text-[var(--muted-foreground)]">
|
||||
Dow XLSX: “External Deadline” — the client-facing one.
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -345,7 +345,7 @@ export function ProjectFormDialog({
|
|||
placeholder="e.g. Celena, Yzabella, Alexia, Majo"
|
||||
{...register("requestor")}
|
||||
/>
|
||||
<p className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<p className="text-[15px] text-[var(--muted-foreground)]">
|
||||
Dow XLSX: “Owner” — the producer running this project.
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ export function AtRiskSection({ projects }: AtRiskSectionProps) {
|
|||
At-Risk Projects
|
||||
</h2>
|
||||
{projects.length > 0 && (
|
||||
<span className="ml-auto rounded-full bg-[var(--accent)]/10 px-2 py-0.5 text-[10px] font-bold text-[var(--accent)]">
|
||||
<span className="ml-auto rounded-full bg-[var(--accent)]/10 px-2 py-0.5 text-[15px] font-bold text-[var(--accent)]">
|
||||
{projects.length}
|
||||
</span>
|
||||
)}
|
||||
|
|
@ -109,7 +109,7 @@ export function AtRiskSection({ projects }: AtRiskSectionProps) {
|
|||
);
|
||||
})}
|
||||
{projects.length > 6 && (
|
||||
<p className="py-2 text-center text-[10px] font-medium text-[var(--muted-foreground)]">
|
||||
<p className="py-2 text-center text-[15px] font-medium text-[var(--muted-foreground)]">
|
||||
+ {projects.length - 6} more at-risk project{projects.length - 6 !== 1 ? "s" : ""}
|
||||
</p>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ export function DeadlinesSection({ deadlines }: DeadlinesSectionProps) {
|
|||
>
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="text-sm font-medium truncate">{item.name}</p>
|
||||
<p className="text-[10px] text-[var(--muted-foreground)] truncate">
|
||||
<p className="text-[15px] text-[var(--muted-foreground)] truncate">
|
||||
{item.projectName}
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -118,7 +118,7 @@ export function DeadlinesSection({ deadlines }: DeadlinesSectionProps) {
|
|||
<p className="text-xs font-semibold text-[var(--accent)]">
|
||||
+{item.daysOverdue}d
|
||||
</p>
|
||||
<p className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<p className="text-[15px] text-[var(--muted-foreground)]">
|
||||
Due {format(new Date(item.dueDate), "MMM d")}
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -126,7 +126,7 @@ export function DeadlinesSection({ deadlines }: DeadlinesSectionProps) {
|
|||
))}
|
||||
{missed.length > 8 && (
|
||||
<div className="px-4 py-2 text-center">
|
||||
<p className="text-[10px] font-medium text-[var(--muted-foreground)]">
|
||||
<p className="text-[15px] font-medium text-[var(--muted-foreground)]">
|
||||
+ {missed.length - 8} more overdue deliverable{missed.length - 8 !== 1 ? "s" : ""}
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ export function KpiStrip({ data }: KpiStripProps) {
|
|||
color: "text-[var(--status-approved)]",
|
||||
bg: "bg-[var(--status-approved)]",
|
||||
detail: (
|
||||
<span className={`flex items-center gap-1 text-[10px] font-medium ${trendColor}`}>
|
||||
<span className={`flex items-center gap-1 text-[15px] font-medium ${trendColor}`}>
|
||||
<TrendIcon className="h-3 w-3" />
|
||||
{trend.trend === "flat"
|
||||
? "Same as last week"
|
||||
|
|
@ -61,7 +61,7 @@ export function KpiStrip({ data }: KpiStripProps) {
|
|||
? "bg-[var(--status-in-review)]"
|
||||
: "bg-[var(--accent)]",
|
||||
detail: (
|
||||
<span className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<span className="text-[15px] text-[var(--muted-foreground)]">
|
||||
{data.deadlines.met.length} met, {data.deadlines.missed.length} missed
|
||||
</span>
|
||||
),
|
||||
|
|
@ -73,7 +73,7 @@ export function KpiStrip({ data }: KpiStripProps) {
|
|||
color: "text-[var(--status-in-progress)]",
|
||||
bg: "bg-[var(--status-in-progress)]",
|
||||
detail: (
|
||||
<span className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<span className="text-[15px] text-[var(--muted-foreground)]">
|
||||
Stages across active projects
|
||||
</span>
|
||||
),
|
||||
|
|
@ -89,7 +89,7 @@ export function KpiStrip({ data }: KpiStripProps) {
|
|||
? "bg-[var(--accent)]"
|
||||
: "bg-[var(--muted-foreground)]",
|
||||
detail: (
|
||||
<span className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<span className="text-[15px] text-[var(--muted-foreground)]">
|
||||
{data.atRiskProjects.length > 0
|
||||
? "Require attention"
|
||||
: "All projects on track"}
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ export function ReportHeader({
|
|||
</div>
|
||||
|
||||
{/* Generated timestamp */}
|
||||
<p className="mt-4 text-[10px] text-[var(--muted-foreground)] print:mt-2">
|
||||
<p className="mt-4 text-[15px] text-[var(--muted-foreground)] print:mt-2">
|
||||
Generated {generatedAt}
|
||||
</p>
|
||||
</header>
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ export function AnnotationTools({
|
|||
<TooltipContent side="bottom" className="text-xs">
|
||||
{tool.label}
|
||||
{tool.shortcut && (
|
||||
<kbd className="ml-1.5 rounded bg-black/20 px-1 py-0.5 font-mono text-[10px]">
|
||||
<kbd className="ml-1.5 rounded bg-black/20 px-1 py-0.5 font-mono text-[15px]">
|
||||
{tool.shortcut}
|
||||
</kbd>
|
||||
)}
|
||||
|
|
@ -137,7 +137,7 @@ export function AnnotationTools({
|
|||
side="bottom"
|
||||
align="start"
|
||||
>
|
||||
<p className="mb-1.5 text-[10px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
<p className="mb-1.5 text-[15px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
Stroke Color
|
||||
</p>
|
||||
<div className="grid grid-cols-4 gap-1">
|
||||
|
|
@ -175,7 +175,7 @@ export function AnnotationTools({
|
|||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom" className="text-xs">
|
||||
Undo <kbd className="ml-1 rounded bg-black/20 px-1 py-0.5 font-mono text-[10px]">Cmd+Z</kbd>
|
||||
Undo <kbd className="ml-1 rounded bg-black/20 px-1 py-0.5 font-mono text-[15px]">Cmd+Z</kbd>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
|
|
@ -192,7 +192,7 @@ export function AnnotationTools({
|
|||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom" className="text-xs">
|
||||
Redo <kbd className="ml-1 rounded bg-black/20 px-1 py-0.5 font-mono text-[10px]">Cmd+Shift+Z</kbd>
|
||||
Redo <kbd className="ml-1 rounded bg-black/20 px-1 py-0.5 font-mono text-[15px]">Cmd+Shift+Z</kbd>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
|
|
@ -234,7 +234,7 @@ export function AnnotationTools({
|
|||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom" className="text-xs">
|
||||
Delete selected
|
||||
<kbd className="ml-1 rounded bg-black/20 px-1 py-0.5 font-mono text-[10px]">Del</kbd>
|
||||
<kbd className="ml-1 rounded bg-black/20 px-1 py-0.5 font-mono text-[15px]">Del</kbd>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ export function ComparisonToolbar({
|
|||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom" className="text-xs">
|
||||
{label} <kbd className="ml-1 rounded bg-[var(--border)] px-1 font-mono text-[10px]">{shortcut}</kbd>
|
||||
{label} <kbd className="ml-1 rounded bg-[var(--border)] px-1 font-mono text-[15px]">{shortcut}</kbd>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
))}
|
||||
|
|
@ -140,7 +140,7 @@ export function ComparisonToolbar({
|
|||
{/* Revision selectors */}
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex items-center gap-1">
|
||||
<span className="text-[10px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
<span className="text-[15px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
A
|
||||
</span>
|
||||
<Select value={leftRevisionKey} onValueChange={onLeftChange}>
|
||||
|
|
@ -162,7 +162,7 @@ export function ComparisonToolbar({
|
|||
</div>
|
||||
|
||||
<div className="flex items-center gap-1">
|
||||
<span className="text-[10px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
<span className="text-[15px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
B
|
||||
</span>
|
||||
<Select value={rightRevisionKey} onValueChange={onRightChange}>
|
||||
|
|
@ -199,7 +199,7 @@ export function ComparisonToolbar({
|
|||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom" className="text-xs">
|
||||
Exit comparison mode <kbd className="ml-1 rounded bg-[var(--border)] px-1 font-mono text-[10px]">Esc</kbd>
|
||||
Exit comparison mode <kbd className="ml-1 rounded bg-[var(--border)] px-1 font-mono text-[15px]">Esc</kbd>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -284,7 +284,7 @@ export function ComparisonViewer({
|
|||
<div className="flex flex-1">
|
||||
{/* Left pane */}
|
||||
<div className="relative flex-1 overflow-hidden border-r border-[var(--border)]">
|
||||
<div className="absolute left-3 top-3 z-10 rounded bg-black/60 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wider text-white/90 backdrop-blur-sm">
|
||||
<div className="absolute left-3 top-3 z-10 rounded bg-black/60 px-2 py-0.5 text-[15px] font-semibold uppercase tracking-wider text-white/90 backdrop-blur-sm">
|
||||
A
|
||||
</div>
|
||||
<div
|
||||
|
|
@ -306,7 +306,7 @@ export function ComparisonViewer({
|
|||
|
||||
{/* Right pane */}
|
||||
<div className="relative flex-1 overflow-hidden">
|
||||
<div className="absolute right-3 top-3 z-10 rounded bg-black/60 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wider text-white/90 backdrop-blur-sm">
|
||||
<div className="absolute right-3 top-3 z-10 rounded bg-black/60 px-2 py-0.5 text-[15px] font-semibold uppercase tracking-wider text-white/90 backdrop-blur-sm">
|
||||
B
|
||||
</div>
|
||||
<div
|
||||
|
|
@ -407,10 +407,10 @@ export function ComparisonViewer({
|
|||
/>
|
||||
|
||||
{/* Labels */}
|
||||
<div className="absolute left-3 top-3 z-10 rounded bg-black/60 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wider text-white/90 backdrop-blur-sm">
|
||||
<div className="absolute left-3 top-3 z-10 rounded bg-black/60 px-2 py-0.5 text-[15px] font-semibold uppercase tracking-wider text-white/90 backdrop-blur-sm">
|
||||
A
|
||||
</div>
|
||||
<div className="absolute right-3 top-3 z-10 rounded bg-black/60 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wider text-white/90 backdrop-blur-sm">
|
||||
<div className="absolute right-3 top-3 z-10 rounded bg-black/60 px-2 py-0.5 text-[15px] font-semibold uppercase tracking-wider text-white/90 backdrop-blur-sm">
|
||||
B ({overlayOpacity}%)
|
||||
</div>
|
||||
|
||||
|
|
@ -435,7 +435,7 @@ export function ComparisonViewer({
|
|||
onFitToView={fitSharedView}
|
||||
onZoomToPreset={() => {}}
|
||||
/>
|
||||
<span className="font-mono text-[10px] text-[var(--muted-foreground)]">
|
||||
<span className="font-mono text-[15px] text-[var(--muted-foreground)]">
|
||||
Press <kbd className="rounded bg-[var(--border)] px-1.5 py-0.5">Space</kbd> to toggle
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -469,7 +469,7 @@ export function ComparisonViewer({
|
|||
/>
|
||||
|
||||
{/* Active label */}
|
||||
<div className="absolute left-1/2 top-3 z-10 -translate-x-1/2 rounded bg-black/60 px-3 py-0.5 text-[10px] font-semibold uppercase tracking-wider text-white/90 backdrop-blur-sm transition-all">
|
||||
<div className="absolute left-1/2 top-3 z-10 -translate-x-1/2 rounded bg-black/60 px-3 py-0.5 text-[15px] font-semibold uppercase tracking-wider text-white/90 backdrop-blur-sm transition-all">
|
||||
{toggleShowRight ? "B" : "A"}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ export function ImageGallery({
|
|||
|
||||
return (
|
||||
<div className="flex items-center gap-1 overflow-x-auto rounded-lg border bg-[var(--card)] p-1.5">
|
||||
<span className="shrink-0 px-1 text-[10px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
<span className="shrink-0 px-1 text-[15px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
Gallery
|
||||
</span>
|
||||
<div className="mx-1 h-8 w-px shrink-0 bg-[var(--border)]" />
|
||||
|
|
@ -48,7 +48,7 @@ export function ImageGallery({
|
|||
className="h-10 w-10 object-cover"
|
||||
/>
|
||||
<div className="absolute bottom-0 left-0 right-0 bg-black/60 px-0.5 text-center">
|
||||
<span className="text-[8px] font-medium text-white">
|
||||
<span className="text-[12px] font-medium text-white">
|
||||
R{img.roundNumber}
|
||||
{img.type === "reference" ? " ref" : ""}
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ export function ImageUploadZone({
|
|||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
className="h-6 px-1.5 text-[10px] text-white hover:bg-white/20 hover:text-white"
|
||||
className="h-6 px-1.5 text-[15px] text-white hover:bg-white/20 hover:text-white"
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
>
|
||||
Replace
|
||||
|
|
@ -124,7 +124,7 @@ export function ImageUploadZone({
|
|||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
className="h-6 px-1.5 text-[10px] text-white hover:bg-red-500/50 hover:text-white"
|
||||
className="h-6 px-1.5 text-[15px] text-white hover:bg-red-500/50 hover:text-white"
|
||||
onClick={handleDelete}
|
||||
>
|
||||
<X className="h-3 w-3" />
|
||||
|
|
@ -177,10 +177,10 @@ export function ImageUploadZone({
|
|||
<p className="text-xs font-medium">
|
||||
{imageType === "reference" ? "Reference Image" : "Current Render"}
|
||||
</p>
|
||||
<p className="mt-0.5 text-[10px] text-[var(--muted-foreground)]">
|
||||
<p className="mt-0.5 text-[15px] text-[var(--muted-foreground)]">
|
||||
Drop file or click to browse
|
||||
</p>
|
||||
<p className="mt-0.5 text-[10px] text-[var(--muted-foreground)]">
|
||||
<p className="mt-0.5 text-[15px] text-[var(--muted-foreground)]">
|
||||
PNG, JPEG, WebP, TIFF — up to 50MB
|
||||
</p>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ export function ImageViewer({
|
|||
onZoomToPreset={viewer.zoomToPreset}
|
||||
/>
|
||||
{dims && (
|
||||
<span className="font-mono text-[10px] text-[var(--muted-foreground)]">
|
||||
<span className="font-mono text-[15px] text-[var(--muted-foreground)]">
|
||||
{dims.width} × {dims.height}
|
||||
</span>
|
||||
)}
|
||||
|
|
@ -80,7 +80,7 @@ export function ImageViewer({
|
|||
|
||||
{/* Pixel info */}
|
||||
{viewer.pixelInfo && (
|
||||
<div className="flex items-center gap-2 font-mono text-[10px] text-[var(--muted-foreground)]">
|
||||
<div className="flex items-center gap-2 font-mono text-[15px] text-[var(--muted-foreground)]">
|
||||
<span>
|
||||
{viewer.pixelInfo.x}, {viewer.pixelInfo.y}
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ export function OverlayControls({
|
|||
|
||||
return (
|
||||
<div className="absolute bottom-4 left-1/2 z-20 flex -translate-x-1/2 items-center gap-3 rounded-lg border bg-[var(--card)]/90 px-4 py-2 shadow-lg backdrop-blur-sm">
|
||||
<span className="text-[10px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
<span className="text-[15px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
Opacity
|
||||
</span>
|
||||
<input
|
||||
|
|
@ -34,7 +34,7 @@ export function OverlayControls({
|
|||
[&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full
|
||||
[&::-webkit-slider-thumb]:bg-[var(--primary)]"
|
||||
/>
|
||||
<span className="min-w-[32px] text-right font-mono text-[10px] text-[var(--muted-foreground)]">
|
||||
<span className="min-w-[32px] text-right font-mono text-[15px] text-[var(--muted-foreground)]">
|
||||
{opacity}%
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ export const RevisionNode = forwardRef<HTMLDivElement, RevisionNodeProps>(
|
|||
<Badge
|
||||
variant="secondary"
|
||||
className={cn(
|
||||
"h-4 px-1 text-[9px] font-medium uppercase",
|
||||
"h-4 px-1 text-[14px] font-medium uppercase",
|
||||
statusStyle.className
|
||||
)}
|
||||
>
|
||||
|
|
@ -152,7 +152,7 @@ export const RevisionNode = forwardRef<HTMLDivElement, RevisionNodeProps>(
|
|||
{revision.isLatest && (
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="h-4 bg-[var(--primary)]/15 px-1 text-[9px] font-medium uppercase text-[var(--primary)]"
|
||||
className="h-4 bg-[var(--primary)]/15 px-1 text-[14px] font-medium uppercase text-[var(--primary)]"
|
||||
>
|
||||
Latest
|
||||
</Badge>
|
||||
|
|
@ -160,7 +160,7 @@ export const RevisionNode = forwardRef<HTMLDivElement, RevisionNodeProps>(
|
|||
{isActive && (
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="h-4 bg-[var(--foreground)]/10 px-1 text-[9px] font-medium uppercase text-[var(--foreground)]"
|
||||
className="h-4 bg-[var(--foreground)]/10 px-1 text-[14px] font-medium uppercase text-[var(--foreground)]"
|
||||
>
|
||||
Current
|
||||
</Badge>
|
||||
|
|
@ -177,7 +177,7 @@ export const RevisionNode = forwardRef<HTMLDivElement, RevisionNodeProps>(
|
|||
/>
|
||||
) : (
|
||||
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded border border-dashed border-[var(--border)] bg-[var(--card)]">
|
||||
<span className="text-[8px] text-[var(--muted-foreground)]">
|
||||
<span className="text-[12px] text-[var(--muted-foreground)]">
|
||||
No img
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -185,13 +185,13 @@ export const RevisionNode = forwardRef<HTMLDivElement, RevisionNodeProps>(
|
|||
|
||||
<div className="min-w-0 flex-1 space-y-0.5">
|
||||
{/* Timestamp */}
|
||||
<p className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<p className="text-[15px] text-[var(--muted-foreground)]">
|
||||
{timeAgo}
|
||||
</p>
|
||||
|
||||
{/* Feedback preview */}
|
||||
{revision.feedbackPreview && (
|
||||
<p className="truncate text-[10px] text-[var(--foreground)]/70">
|
||||
<p className="truncate text-[15px] text-[var(--foreground)]/70">
|
||||
{revision.feedbackPreview}
|
||||
</p>
|
||||
)}
|
||||
|
|
@ -205,7 +205,7 @@ export const RevisionNode = forwardRef<HTMLDivElement, RevisionNodeProps>(
|
|||
<TooltipTrigger asChild>
|
||||
<button
|
||||
onClick={handleAnnotationClick}
|
||||
className="inline-flex items-center gap-0.5 text-[10px] text-[var(--muted-foreground)] hover:text-[var(--foreground)] transition-colors"
|
||||
className="inline-flex items-center gap-0.5 text-[15px] text-[var(--muted-foreground)] hover:text-[var(--foreground)] transition-colors"
|
||||
>
|
||||
<PenLine className="h-3 w-3" />
|
||||
{revision.annotationCount}
|
||||
|
|
@ -219,7 +219,7 @@ export const RevisionNode = forwardRef<HTMLDivElement, RevisionNodeProps>(
|
|||
{/* Comment count (stage-level) */}
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span className="inline-flex items-center gap-0.5 text-[10px] text-[var(--muted-foreground)]">
|
||||
<span className="inline-flex items-center gap-0.5 text-[15px] text-[var(--muted-foreground)]">
|
||||
<MessageSquare className="h-3 w-3" />
|
||||
{revision.stageCommentCount}
|
||||
</span>
|
||||
|
|
@ -231,13 +231,13 @@ export const RevisionNode = forwardRef<HTMLDivElement, RevisionNodeProps>(
|
|||
|
||||
{/* Decision record for approved/rejected */}
|
||||
{revision.status === "APPROVED" && (
|
||||
<span className="inline-flex items-center gap-0.5 text-[10px] text-[var(--status-approved)]">
|
||||
<span className="inline-flex items-center gap-0.5 text-[15px] text-[var(--status-approved)]">
|
||||
<CheckCircle2 className="h-3 w-3" />
|
||||
Approved
|
||||
</span>
|
||||
)}
|
||||
{revision.status === "CHANGES_REQUESTED" && (
|
||||
<span className="inline-flex items-center gap-0.5 text-[10px] text-[var(--status-blocked)]">
|
||||
<span className="inline-flex items-center gap-0.5 text-[15px] text-[var(--status-blocked)]">
|
||||
<XCircle className="h-3 w-3" />
|
||||
Changes
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -215,7 +215,7 @@ export function RevisionTimeline({
|
|||
{/* ── Filter bar (embedded gets a compact filter row) ─────── */}
|
||||
{!embedded ? null : enrichedRevisions.length > 0 ? (
|
||||
<div className="flex items-center justify-between border-b px-3 py-1.5">
|
||||
<span className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<span className="text-[15px] text-[var(--muted-foreground)]">
|
||||
{enrichedRevisions.length} round{enrichedRevisions.length !== 1 ? "s" : ""}
|
||||
</span>
|
||||
<div className="flex items-center gap-1">
|
||||
|
|
@ -242,7 +242,7 @@ export function RevisionTimeline({
|
|||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
className="h-6 text-[10px]"
|
||||
className="h-6 text-[15px]"
|
||||
onClick={onCreateRevision}
|
||||
disabled={isCreatingRevision}
|
||||
>
|
||||
|
|
@ -300,7 +300,7 @@ export function RevisionTimeline({
|
|||
New Round
|
||||
</Button>
|
||||
) : (
|
||||
<p className="text-[10px] text-[var(--muted-foreground)]/60">
|
||||
<p className="text-[15px] text-[var(--muted-foreground)]/60">
|
||||
Submit a revision to start tracking history
|
||||
</p>
|
||||
)}
|
||||
|
|
@ -316,7 +316,7 @@ export function RevisionTimeline({
|
|||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
className="h-6 text-[10px]"
|
||||
className="h-6 text-[15px]"
|
||||
onClick={() => setFilterMode("all")}
|
||||
>
|
||||
Show all rounds
|
||||
|
|
@ -364,11 +364,11 @@ export function RevisionTimeline({
|
|||
<div className="flex items-center justify-between border-b px-3 py-2">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<History className="h-3.5 w-3.5 text-[var(--muted-foreground)]" />
|
||||
<span className="text-[10px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
<span className="text-[15px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
Revision History
|
||||
</span>
|
||||
{enrichedRevisions.length > 0 && (
|
||||
<span className="ml-1 rounded-full bg-[var(--foreground)]/10 px-1.5 text-[9px] font-medium text-[var(--muted-foreground)]">
|
||||
<span className="ml-1 rounded-full bg-[var(--foreground)]/10 px-1.5 text-[14px] font-medium text-[var(--muted-foreground)]">
|
||||
{enrichedRevisions.length}
|
||||
</span>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -235,14 +235,14 @@ export function VideoAnnotationLayer({
|
|||
|
||||
<div className="flex items-center gap-1 border-l border-[var(--border)] pl-2">
|
||||
<Clock className="h-3 w-3 text-[var(--muted-foreground)]" />
|
||||
<span className="font-mono text-[10px] tabular-nums text-[var(--muted-foreground)]">
|
||||
<span className="font-mono text-[15px] tabular-nums text-[var(--muted-foreground)]">
|
||||
{formatTimecode(currentTime, fps)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={() => setShowAllAnnotations((v) => !v)}
|
||||
className={`rounded px-1.5 py-0.5 text-[10px] font-medium transition-colors ${
|
||||
className={`rounded px-1.5 py-0.5 text-[15px] font-medium transition-colors ${
|
||||
showAllAnnotations
|
||||
? "bg-[var(--primary)] text-[var(--primary-foreground)]"
|
||||
: "text-[var(--muted-foreground)] hover:bg-[var(--muted)]"
|
||||
|
|
@ -505,10 +505,10 @@ export function VideoAnnotationLayer({
|
|||
className="h-2 w-2 rounded-full"
|
||||
style={{ backgroundColor: ann.color }}
|
||||
/>
|
||||
<span className="text-[10px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
<span className="text-[15px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
Add Comment
|
||||
</span>
|
||||
<span className="ml-auto flex items-center gap-1 font-mono text-[9px] text-[var(--muted-foreground)]">
|
||||
<span className="ml-auto flex items-center gap-1 font-mono text-[14px] text-[var(--muted-foreground)]">
|
||||
<Clock className="h-2.5 w-2.5" />
|
||||
{currentTime.toFixed(2)}s
|
||||
</span>
|
||||
|
|
@ -539,12 +539,12 @@ export function VideoAnnotationLayer({
|
|||
autoFocus
|
||||
/>
|
||||
<div className="mt-2 flex items-center justify-between">
|
||||
<span className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<span className="text-[15px] text-[var(--muted-foreground)]">
|
||||
Enter to save
|
||||
</span>
|
||||
<Button
|
||||
size="sm"
|
||||
className="h-6 px-2 text-[10px]"
|
||||
className="h-6 px-2 text-[15px]"
|
||||
onClick={() => ann.commitPendingAnnotation(ann.commentValue)}
|
||||
>
|
||||
<Send className="mr-1 h-2.5 w-2.5" />
|
||||
|
|
|
|||
|
|
@ -182,7 +182,7 @@ export function VideoControls({
|
|||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
className="h-7 px-1.5 text-[10px] font-mono tabular-nums"
|
||||
className="h-7 px-1.5 text-[15px] font-mono tabular-nums"
|
||||
onClick={() => {
|
||||
const idx = SPEED_OPTIONS.indexOf(playbackSpeed);
|
||||
const next = SPEED_OPTIONS[(idx + 1) % SPEED_OPTIONS.length];
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ export function VideoFrameDisplay({
|
|||
className,
|
||||
}: VideoFrameDisplayProps) {
|
||||
return (
|
||||
<span className={`font-mono text-[10px] tabular-nums ${className ?? ""}`}>
|
||||
<span className={`font-mono text-[15px] tabular-nums ${className ?? ""}`}>
|
||||
{toTimecode(currentTime, fps)}
|
||||
<span className="text-[var(--muted-foreground)] opacity-50"> / </span>
|
||||
{toTimecode(duration, fps)}
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ export function VideoTimeline({
|
|||
{/* Hover time tooltip */}
|
||||
{hoverTime !== null && (
|
||||
<div
|
||||
className="pointer-events-none absolute -top-7 z-10 -translate-x-1/2 rounded bg-[var(--popover)] px-1.5 py-0.5 text-[10px] font-mono text-[var(--popover-foreground)] shadow-md"
|
||||
className="pointer-events-none absolute -top-7 z-10 -translate-x-1/2 rounded bg-[var(--popover)] px-1.5 py-0.5 text-[15px] font-mono text-[var(--popover-foreground)] shadow-md"
|
||||
style={{ left: hoverX }}
|
||||
>
|
||||
{formatTime(hoverTime)}
|
||||
|
|
|
|||
|
|
@ -152,22 +152,22 @@ export function VideoUploadZone({
|
|||
{/* Status badge */}
|
||||
<div className="absolute bottom-1 left-1 flex items-center gap-1">
|
||||
{existingVideo.status === "processing" && (
|
||||
<span className="flex items-center gap-1 rounded bg-yellow-500/80 px-1.5 py-0.5 text-[9px] font-medium text-white">
|
||||
<span className="flex items-center gap-1 rounded bg-yellow-500/80 px-1.5 py-0.5 text-[14px] font-medium text-white">
|
||||
<Loader2 className="h-2.5 w-2.5 animate-spin" />
|
||||
Processing
|
||||
</span>
|
||||
)}
|
||||
{existingVideo.status === "ready" && (
|
||||
<span className="rounded bg-green-600/80 px-1.5 py-0.5 text-[9px] font-medium text-white">
|
||||
<span className="rounded bg-green-600/80 px-1.5 py-0.5 text-[14px] font-medium text-white">
|
||||
Ready
|
||||
</span>
|
||||
)}
|
||||
{existingVideo.status === "failed" && (
|
||||
<span className="rounded bg-red-600/80 px-1.5 py-0.5 text-[9px] font-medium text-white">
|
||||
<span className="rounded bg-red-600/80 px-1.5 py-0.5 text-[14px] font-medium text-white">
|
||||
Failed
|
||||
</span>
|
||||
)}
|
||||
<span className="rounded bg-black/60 px-1.5 py-0.5 font-mono text-[9px] text-white">
|
||||
<span className="rounded bg-black/60 px-1.5 py-0.5 font-mono text-[14px] text-white">
|
||||
{formatDuration(existingVideo.duration)}
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -177,7 +177,7 @@ export function VideoUploadZone({
|
|||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
className="h-6 px-1.5 text-[10px] text-white hover:bg-white/20 hover:text-white"
|
||||
className="h-6 px-1.5 text-[15px] text-white hover:bg-white/20 hover:text-white"
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
>
|
||||
Replace
|
||||
|
|
@ -185,7 +185,7 @@ export function VideoUploadZone({
|
|||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
className="h-6 px-1.5 text-[10px] text-white hover:bg-red-500/50 hover:text-white"
|
||||
className="h-6 px-1.5 text-[15px] text-white hover:bg-red-500/50 hover:text-white"
|
||||
onClick={handleDelete}
|
||||
>
|
||||
<X className="h-3 w-3" />
|
||||
|
|
@ -240,10 +240,10 @@ export function VideoUploadZone({
|
|||
<p className="text-xs font-medium">
|
||||
{videoType === "video" ? "Video" : "Reference Video"}
|
||||
</p>
|
||||
<p className="mt-0.5 text-[10px] text-[var(--muted-foreground)]">
|
||||
<p className="mt-0.5 text-[15px] text-[var(--muted-foreground)]">
|
||||
Drop file or click to browse
|
||||
</p>
|
||||
<p className="mt-0.5 text-[10px] text-[var(--muted-foreground)]">
|
||||
<p className="mt-0.5 text-[15px] text-[var(--muted-foreground)]">
|
||||
MP4 — up to 500MB
|
||||
</p>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -179,10 +179,10 @@ export function WipeDivider({
|
|||
</div>
|
||||
|
||||
{/* Labels */}
|
||||
<div className="absolute left-3 top-3 z-10 rounded bg-black/60 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wider text-white/90 backdrop-blur-sm">
|
||||
<div className="absolute left-3 top-3 z-10 rounded bg-black/60 px-2 py-0.5 text-[15px] font-semibold uppercase tracking-wider text-white/90 backdrop-blur-sm">
|
||||
A
|
||||
</div>
|
||||
<div className="absolute right-3 top-3 z-10 rounded bg-black/60 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wider text-white/90 backdrop-blur-sm">
|
||||
<div className="absolute right-3 top-3 z-10 rounded bg-black/60 px-2 py-0.5 text-[15px] font-semibold uppercase tracking-wider text-white/90 backdrop-blur-sm">
|
||||
B
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@ export function RevisionList({ stageId }: { stageId: string }) {
|
|||
</span>
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className={cn("text-[10px]", config.className)}
|
||||
className={cn("text-[15px]", config.className)}
|
||||
>
|
||||
<Icon className="mr-0.5 h-3 w-3" />
|
||||
{config.label}
|
||||
|
|
@ -190,7 +190,7 @@ export function RevisionList({ stageId }: { stageId: string }) {
|
|||
|
||||
{rev.feedbackNotes && (
|
||||
<div className="rounded bg-[var(--muted)] p-2">
|
||||
<p className="text-[10px] font-semibold uppercase text-[var(--muted-foreground)]">
|
||||
<p className="text-[15px] font-semibold uppercase text-[var(--muted-foreground)]">
|
||||
Feedback
|
||||
</p>
|
||||
<p className="mt-0.5 text-xs">{rev.feedbackNotes}</p>
|
||||
|
|
@ -214,7 +214,7 @@ export function RevisionList({ stageId }: { stageId: string }) {
|
|||
<div className="flex gap-1.5">
|
||||
<Button
|
||||
size="sm"
|
||||
className="h-6 text-[10px]"
|
||||
className="h-6 text-[15px]"
|
||||
disabled={updateRevision.isPending}
|
||||
onClick={() => handleStatusUpdate(rev.id, "APPROVED")}
|
||||
>
|
||||
|
|
@ -224,7 +224,7 @@ export function RevisionList({ stageId }: { stageId: string }) {
|
|||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="h-6 text-[10px]"
|
||||
className="h-6 text-[15px]"
|
||||
disabled={updateRevision.isPending}
|
||||
onClick={() =>
|
||||
handleStatusUpdate(rev.id, "CHANGES_REQUESTED")
|
||||
|
|
@ -237,7 +237,7 @@ export function RevisionList({ stageId }: { stageId: string }) {
|
|||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
className="h-6 text-[10px]"
|
||||
className="h-6 text-[15px]"
|
||||
disabled={updateRevision.isPending}
|
||||
onClick={() => handleStatusUpdate(rev.id, "IN_REVIEW")}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -142,12 +142,12 @@ function ResultCard({
|
|||
<p className="text-sm font-medium truncate">{result.name}</p>
|
||||
<div className="flex items-center gap-1.5 mt-0.5">
|
||||
{result.projectCode && (
|
||||
<span className="font-mono text-[10px] text-[var(--muted-foreground)]">
|
||||
<span className="font-mono text-[15px] text-[var(--muted-foreground)]">
|
||||
{result.projectCode}
|
||||
</span>
|
||||
)}
|
||||
{result.projectName && !isProject && (
|
||||
<span className="text-[10px] text-[var(--muted-foreground)] truncate">
|
||||
<span className="text-[15px] text-[var(--muted-foreground)] truncate">
|
||||
in {result.projectName}
|
||||
</span>
|
||||
)}
|
||||
|
|
@ -157,7 +157,7 @@ function ResultCard({
|
|||
<div className="flex items-center gap-1.5 shrink-0">
|
||||
<Badge
|
||||
className={cn(
|
||||
"text-[9px] px-1.5 py-0 h-4 font-semibold",
|
||||
"text-[14px] px-1.5 py-0 h-4 font-semibold",
|
||||
statusColor(result.status)
|
||||
)}
|
||||
>
|
||||
|
|
@ -168,21 +168,21 @@ function ResultCard({
|
|||
</div>
|
||||
|
||||
{result.description && (
|
||||
<p className="mt-1.5 text-[11px] text-[var(--muted-foreground)] line-clamp-2">
|
||||
<p className="mt-1.5 text-[17px] text-[var(--muted-foreground)] line-clamp-2">
|
||||
{result.description}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div className="mt-2 flex items-center gap-2">
|
||||
<span className="label-upper !text-[8px]">
|
||||
<span className="label-upper !text-[12px]">
|
||||
{isProject ? "Project" : "Deliverable"}
|
||||
</span>
|
||||
{relevancePct < 100 && (
|
||||
<span className="text-[9px] text-[var(--muted-foreground)]">
|
||||
<span className="text-[14px] text-[var(--muted-foreground)]">
|
||||
{relevancePct}% match
|
||||
</span>
|
||||
)}
|
||||
<Badge variant="outline" className="text-[8px] px-1 py-0 h-3.5">
|
||||
<Badge variant="outline" className="text-[12px] px-1 py-0 h-3.5">
|
||||
{result.priority}
|
||||
</Badge>
|
||||
</div>
|
||||
|
|
@ -328,7 +328,7 @@ export function SmartSearchPanel({
|
|||
{ollamaHealth?.available ? (
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="h-4 px-1.5 text-[8px] font-semibold border-emerald-500/30 text-emerald-600 dark:text-emerald-400"
|
||||
className="h-4 px-1.5 text-[12px] font-semibold border-emerald-500/30 text-emerald-600 dark:text-emerald-400"
|
||||
>
|
||||
<Zap className="mr-0.5 h-2 w-2" />
|
||||
AI Active
|
||||
|
|
@ -336,7 +336,7 @@ export function SmartSearchPanel({
|
|||
) : (
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="h-4 px-1.5 text-[8px] font-semibold border-amber-500/30 text-amber-600 dark:text-amber-400"
|
||||
className="h-4 px-1.5 text-[12px] font-semibold border-amber-500/30 text-amber-600 dark:text-amber-400"
|
||||
>
|
||||
Text Only
|
||||
</Badge>
|
||||
|
|
@ -347,7 +347,7 @@ export function SmartSearchPanel({
|
|||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-7 text-[10px]"
|
||||
className="h-7 text-[15px]"
|
||||
onClick={handleClear}
|
||||
>
|
||||
Clear
|
||||
|
|
@ -376,7 +376,7 @@ export function SmartSearchPanel({
|
|||
<h3 className="text-sm font-semibold mb-1">
|
||||
Ask anything about your projects
|
||||
</h3>
|
||||
<p className="text-[11px] text-[var(--muted-foreground)] max-w-xs mb-6">
|
||||
<p className="text-[17px] text-[var(--muted-foreground)] max-w-xs mb-6">
|
||||
Search using natural language. Ask about project status, find
|
||||
deliverables, or explore your production data.
|
||||
</p>
|
||||
|
|
@ -415,7 +415,7 @@ export function SmartSearchPanel({
|
|||
{entry.searchType && (
|
||||
<div className="flex items-center gap-1.5">
|
||||
{searchTypeIcon(entry.searchType)}
|
||||
<span className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<span className="text-[15px] text-[var(--muted-foreground)]">
|
||||
{searchTypeLabel(entry.searchType)}
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -492,7 +492,7 @@ export function SmartSearchPanel({
|
|||
)}
|
||||
</Button>
|
||||
</form>
|
||||
<p className="mt-1.5 text-center text-[9px] text-[var(--muted-foreground)]">
|
||||
<p className="mt-1.5 text-center text-[14px] text-[var(--muted-foreground)]">
|
||||
{ollamaHealth?.available
|
||||
? "Powered by local AI — your data never leaves the network"
|
||||
: "AI unavailable — using text search fallback"}
|
||||
|
|
|
|||
|
|
@ -159,13 +159,13 @@ export function StageDatePopover({
|
|||
|
||||
<PopoverContent align="start" className="w-64 space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="text-[10px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
<p className="text-[15px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
Stage Dates
|
||||
</p>
|
||||
{manualSchedule && (
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="h-5 gap-0.5 border-[var(--accent)] text-[10px] text-[var(--accent)]"
|
||||
className="h-5 gap-0.5 border-[var(--accent)] text-[15px] text-[var(--accent)]"
|
||||
>
|
||||
<Pin className="h-2.5 w-2.5" />
|
||||
Pinned
|
||||
|
|
@ -177,7 +177,7 @@ export function StageDatePopover({
|
|||
<div>
|
||||
<label
|
||||
htmlFor={`start-${stageId}`}
|
||||
className="mb-1 block text-[10px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]"
|
||||
className="mb-1 block text-[15px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]"
|
||||
>
|
||||
Start
|
||||
</label>
|
||||
|
|
@ -192,7 +192,7 @@ export function StageDatePopover({
|
|||
<div>
|
||||
<label
|
||||
htmlFor={`due-${stageId}`}
|
||||
className="mb-1 block text-[10px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]"
|
||||
className="mb-1 block text-[15px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]"
|
||||
>
|
||||
Due
|
||||
</label>
|
||||
|
|
@ -207,7 +207,7 @@ export function StageDatePopover({
|
|||
</div>
|
||||
|
||||
{scheduleConflict && (
|
||||
<div className="flex items-center gap-1.5 rounded bg-[var(--status-blocked)]/10 px-2 py-1.5 text-[10px] text-[var(--status-blocked)]">
|
||||
<div className="flex items-center gap-1.5 rounded bg-[var(--status-blocked)]/10 px-2 py-1.5 text-[15px] text-[var(--status-blocked)]">
|
||||
<AlertTriangle className="h-3 w-3 shrink-0" />
|
||||
Schedule conflict — not enough time for prerequisites
|
||||
</div>
|
||||
|
|
@ -244,7 +244,7 @@ export function StageDatePopover({
|
|||
{scheduleDelta != null && scheduleDelta !== 0 && (
|
||||
<span
|
||||
className={cn(
|
||||
"inline-flex items-center rounded-full px-1.5 py-0.5 text-[10px] font-semibold tabular-nums",
|
||||
"inline-flex items-center rounded-full px-1.5 py-0.5 text-[15px] font-semibold tabular-nums",
|
||||
scheduleDelta > 0
|
||||
? "bg-[var(--status-in-review)]/15 text-[var(--status-in-review)]"
|
||||
: "bg-[var(--status-blocked)]/15 text-[var(--status-blocked)]"
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ export function StageDetailSheet({
|
|||
{(stage.stageDefinition?.isCriticalGate ?? stage.template.isCriticalGate) && (
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="h-5 gap-0.5 border-[var(--accent)] text-[10px] text-[var(--accent)]"
|
||||
className="h-5 gap-0.5 border-[var(--accent)] text-[15px] text-[var(--accent)]"
|
||||
>
|
||||
<Shield className="h-3 w-3" />
|
||||
GATE
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { Slot } from "radix-ui"
|
|||
import { cn } from "@/lib/utils"
|
||||
|
||||
const badgeVariants = cva(
|
||||
"inline-flex items-center justify-center rounded-full border border-transparent px-2.5 py-0.5 text-[10px] font-semibold tracking-[0.06em] uppercase w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
|
||||
"inline-flex items-center justify-center rounded-full border border-transparent px-2.5 py-0.5 text-[15px] font-semibold tracking-[0.06em] uppercase w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
|
|
|
|||
|
|
@ -292,7 +292,7 @@ export function DeliverableTable({
|
|||
<Users className="h-3.5 w-3.5 shrink-0 text-[var(--muted-foreground)]" />
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{assignees.map((a) => (
|
||||
<Badge key={a.id} variant="secondary" className="text-[10px]">
|
||||
<Badge key={a.id} variant="secondary" className="text-[15px]">
|
||||
{a.user?.name ?? a.user?.email ?? "Unknown"}
|
||||
</Badge>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -211,7 +211,7 @@ export function GanttTimeline({
|
|||
data-no-drag
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="absolute top-2 right-4 z-50 h-7 text-[10px] gap-1.5 bg-[var(--card)]/90 backdrop-blur-sm shadow-lg"
|
||||
className="absolute top-2 right-4 z-50 h-7 text-[15px] gap-1.5 bg-[var(--card)]/90 backdrop-blur-sm shadow-lg"
|
||||
onClick={scrollToToday}
|
||||
>
|
||||
<CalendarDays className="h-3 w-3" />
|
||||
|
|
@ -241,7 +241,7 @@ export function GanttTimeline({
|
|||
style={{ height: HEADER_HEIGHT }}
|
||||
>
|
||||
<div
|
||||
className="flex items-end px-3 pb-1 text-[10px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]"
|
||||
className="flex items-end px-3 pb-1 text-[15px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]"
|
||||
style={{ height: HEADER_HEIGHT }}
|
||||
>
|
||||
Deliverable
|
||||
|
|
@ -272,7 +272,7 @@ export function GanttTimeline({
|
|||
{weekHeaders.map((wh) => (
|
||||
<div
|
||||
key={wh.startIndex}
|
||||
className="absolute flex items-center border-l border-r border-[var(--border)]/40 pl-1.5 text-[9px] font-semibold text-[var(--muted-foreground)]"
|
||||
className="absolute flex items-center border-l border-r border-[var(--border)]/40 pl-1.5 text-[14px] font-semibold text-[var(--muted-foreground)]"
|
||||
style={{
|
||||
left: wh.startIndex * DAY_WIDTH,
|
||||
width: wh.span * DAY_WIDTH,
|
||||
|
|
@ -290,7 +290,7 @@ export function GanttTimeline({
|
|||
<div
|
||||
key={day.dayIndex}
|
||||
className={cn(
|
||||
"absolute flex flex-col items-center justify-center border-l text-[8px] leading-tight",
|
||||
"absolute flex flex-col items-center justify-center border-l text-[12px] leading-tight",
|
||||
day.isToday
|
||||
? "bg-[var(--accent)]/20 font-bold text-[var(--accent)]"
|
||||
: day.isWeekendDay
|
||||
|
|
@ -304,7 +304,7 @@ export function GanttTimeline({
|
|||
}}
|
||||
>
|
||||
<span className="font-semibold">{day.dayLabel}</span>
|
||||
<span className="text-[7px] opacity-60">{day.dayOfWeek}</span>
|
||||
<span className="text-[11px] opacity-60">{day.dayOfWeek}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -382,7 +382,7 @@ export function GanttTimeline({
|
|||
>
|
||||
{/* Stage name on bar (if wide enough) */}
|
||||
{barPixelWidth > 60 && (
|
||||
<span className="text-[8px] font-semibold text-white/90 pl-1.5 truncate">
|
||||
<span className="text-[12px] font-semibold text-white/90 pl-1.5 truncate">
|
||||
{stage.stageDefinition?.name ?? stage.template.name}
|
||||
</span>
|
||||
)}
|
||||
|
|
@ -391,7 +391,7 @@ export function GanttTimeline({
|
|||
{isActive && daysRemaining !== null && barPixelWidth > 35 && (
|
||||
<span
|
||||
className={cn(
|
||||
"absolute -top-2.5 -right-1 text-[7px] font-bold px-1 rounded-full leading-none py-0.5",
|
||||
"absolute -top-2.5 -right-1 text-[11px] font-bold px-1 rounded-full leading-none py-0.5",
|
||||
isOverdue
|
||||
? "bg-red-500 text-white"
|
||||
: daysRemaining <= 2
|
||||
|
|
@ -412,17 +412,17 @@ export function GanttTimeline({
|
|||
<p className="font-semibold">{stage.stageDefinition?.name ?? stage.template.name}</p>
|
||||
<p className="text-xs">{stage.status.replace(/_/g, " ")}</p>
|
||||
{stage.startDate && (
|
||||
<p className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<p className="text-[15px] text-[var(--muted-foreground)]">
|
||||
Started: {format(new Date(stage.startDate), "MMM d, yyyy")}
|
||||
</p>
|
||||
)}
|
||||
{stage.dueDate && (
|
||||
<p className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<p className="text-[15px] text-[var(--muted-foreground)]">
|
||||
Due: {format(new Date(stage.dueDate), "MMM d, yyyy")}
|
||||
</p>
|
||||
)}
|
||||
{stage.completedDate && (
|
||||
<p className="text-[10px] text-emerald-400">
|
||||
<p className="text-[15px] text-emerald-400">
|
||||
Completed: {format(new Date(stage.completedDate), "MMM d, yyyy")}
|
||||
</p>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -180,7 +180,7 @@ export function KanbanBoard({
|
|||
<Badge
|
||||
variant="secondary"
|
||||
className={cn(
|
||||
"text-[10px]",
|
||||
"text-[15px]",
|
||||
PRIORITY_STYLES[deliv.priority]
|
||||
)}
|
||||
>
|
||||
|
|
@ -205,7 +205,7 @@ export function KanbanBoard({
|
|||
<Badge
|
||||
key={a.id}
|
||||
variant="secondary"
|
||||
className="text-[10px]"
|
||||
className="text-[15px]"
|
||||
>
|
||||
{a.user?.name ?? a.user?.email ?? "Unknown"}
|
||||
</Badge>
|
||||
|
|
|
|||
|
|
@ -296,7 +296,7 @@ export function ProductionTimeline({
|
|||
data-no-drag
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="absolute top-2 right-4 z-50 h-7 text-[10px] gap-1.5 bg-[var(--card)]/90 backdrop-blur-sm shadow-lg"
|
||||
className="absolute top-2 right-4 z-50 h-7 text-[15px] gap-1.5 bg-[var(--card)]/90 backdrop-blur-sm shadow-lg"
|
||||
onClick={scrollToToday}
|
||||
>
|
||||
<CalendarDays className="h-3 w-3" />
|
||||
|
|
@ -305,7 +305,7 @@ export function ProductionTimeline({
|
|||
|
||||
{/* Drag hint */}
|
||||
{isDragging && (
|
||||
<div className="absolute top-2 left-1/2 -translate-x-1/2 z-50 text-[9px] font-medium text-[var(--muted-foreground)] bg-[var(--card)]/90 backdrop-blur-sm px-2 py-0.5 rounded-full border shadow-sm flex items-center gap-1">
|
||||
<div className="absolute top-2 left-1/2 -translate-x-1/2 z-50 text-[14px] font-medium text-[var(--muted-foreground)] bg-[var(--card)]/90 backdrop-blur-sm px-2 py-0.5 rounded-full border shadow-sm flex items-center gap-1">
|
||||
<GripHorizontal className="h-3 w-3" />
|
||||
Drag to pan
|
||||
</div>
|
||||
|
|
@ -334,7 +334,7 @@ export function ProductionTimeline({
|
|||
style={{ height: HEADER_HEIGHT }}
|
||||
>
|
||||
<div
|
||||
className="flex items-end px-3 pb-1 text-[10px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]"
|
||||
className="flex items-end px-3 pb-1 text-[15px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]"
|
||||
style={{ height: HEADER_HEIGHT }}
|
||||
>
|
||||
Project / Deliverable
|
||||
|
|
@ -369,24 +369,24 @@ export function ProductionTimeline({
|
|||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<span className="text-[9px] font-mono text-[var(--muted-foreground)]">
|
||||
<span className="text-[14px] font-mono text-[var(--muted-foreground)]">
|
||||
{p.projectCode}
|
||||
</span>
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className={cn(
|
||||
"h-3.5 text-[8px] px-1",
|
||||
"h-3.5 text-[12px] px-1",
|
||||
PROJECT_STATUS_STYLES[p.status] || ""
|
||||
)}
|
||||
>
|
||||
{p.status}
|
||||
</Badge>
|
||||
<span className="text-[8px] text-[var(--muted-foreground)]">
|
||||
<span className="text-[12px] text-[var(--muted-foreground)]">
|
||||
{p.completionPercent}%
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<span className="text-[9px] text-[var(--muted-foreground)] shrink-0">
|
||||
<span className="text-[14px] text-[var(--muted-foreground)] shrink-0">
|
||||
{p.deliverableCount}
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -402,7 +402,7 @@ export function ProductionTimeline({
|
|||
>
|
||||
<Link
|
||||
href={`/projects/${row.project.id}/deliverables/${d.id}`}
|
||||
className="text-[11px] truncate hover:underline"
|
||||
className="text-[17px] truncate hover:underline"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{d.name}
|
||||
|
|
@ -424,7 +424,7 @@ export function ProductionTimeline({
|
|||
{weekHeaders.map((wh) => (
|
||||
<div
|
||||
key={wh.startIndex}
|
||||
className="absolute flex items-center border-l border-r border-[var(--border)]/40 pl-1.5 text-[9px] font-semibold text-[var(--muted-foreground)]"
|
||||
className="absolute flex items-center border-l border-r border-[var(--border)]/40 pl-1.5 text-[14px] font-semibold text-[var(--muted-foreground)]"
|
||||
style={{
|
||||
left: wh.startIndex * DAY_WIDTH,
|
||||
width: wh.span * DAY_WIDTH,
|
||||
|
|
@ -443,7 +443,7 @@ export function ProductionTimeline({
|
|||
<div
|
||||
key={day.dayIndex}
|
||||
className={cn(
|
||||
"absolute flex flex-col items-center justify-center border-l text-[7px] leading-tight",
|
||||
"absolute flex flex-col items-center justify-center border-l text-[11px] leading-tight",
|
||||
day.isToday
|
||||
? "bg-[var(--accent)]/20 font-bold text-[var(--accent)]"
|
||||
: day.isWeekendDay
|
||||
|
|
@ -457,7 +457,7 @@ export function ProductionTimeline({
|
|||
}}
|
||||
>
|
||||
<span className="font-semibold">{day.dayLabel}</span>
|
||||
<span className="text-[6px] opacity-60">{day.dayOfWeek}</span>
|
||||
<span className="text-[9px] opacity-60">{day.dayOfWeek}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -490,7 +490,7 @@ export function ProductionTimeline({
|
|||
{/* Today label */}
|
||||
{todayIndex >= 0 && todayIndex < totalDays && (
|
||||
<div
|
||||
className="absolute z-20 text-[7px] font-bold text-[var(--accent)] bg-[var(--accent)]/15 px-1 rounded"
|
||||
className="absolute z-20 text-[11px] font-bold text-[var(--accent)] bg-[var(--accent)]/15 px-1 rounded"
|
||||
style={{
|
||||
left: todayIndex * DAY_WIDTH + DAY_WIDTH / 2 - 12,
|
||||
top: 2,
|
||||
|
|
@ -686,14 +686,14 @@ export function ProductionTimeline({
|
|||
>
|
||||
{/* Stage name */}
|
||||
{barPx > 55 && (
|
||||
<span className="text-[7px] font-semibold text-white/90 pl-1 truncate">
|
||||
<span className="text-[11px] font-semibold text-white/90 pl-1 truncate">
|
||||
{stage.stageDefinition?.name ?? stage.template.name}
|
||||
</span>
|
||||
)}
|
||||
|
||||
{/* Artist initials */}
|
||||
{barPx > 80 && stage.assignments.length > 0 && (
|
||||
<span className="ml-auto text-[6px] font-bold text-white/60 pr-0.5">
|
||||
<span className="ml-auto text-[9px] font-bold text-white/60 pr-0.5">
|
||||
{stage.assignments.map((a) => getInitials(a.user.name)).join(" ")}
|
||||
</span>
|
||||
)}
|
||||
|
|
@ -702,7 +702,7 @@ export function ProductionTimeline({
|
|||
{isActive && daysRemaining !== null && barPx > 30 && (
|
||||
<span
|
||||
className={cn(
|
||||
"absolute -top-2.5 -right-1 text-[6px] font-bold px-1 rounded-full leading-none py-0.5",
|
||||
"absolute -top-2.5 -right-1 text-[9px] font-bold px-1 rounded-full leading-none py-0.5",
|
||||
isOverdue
|
||||
? "bg-red-500 text-white"
|
||||
: daysRemaining <= 2
|
||||
|
|
@ -723,17 +723,17 @@ export function ProductionTimeline({
|
|||
<p className="font-semibold">{stage.stageDefinition?.name ?? stage.template.name}</p>
|
||||
<p className="text-xs">{stage.status.replace(/_/g, " ")}</p>
|
||||
{stage.assignments.length > 0 && (
|
||||
<p className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<p className="text-[15px] text-[var(--muted-foreground)]">
|
||||
{stage.assignments.map((a) => a.user.name).join(", ")}
|
||||
</p>
|
||||
)}
|
||||
{stage.startDate && (
|
||||
<p className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<p className="text-[15px] text-[var(--muted-foreground)]">
|
||||
Started: {format(new Date(stage.startDate), "MMM d")}
|
||||
</p>
|
||||
)}
|
||||
{stage.dueDate && (
|
||||
<p className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<p className="text-[15px] text-[var(--muted-foreground)]">
|
||||
Due: {format(new Date(stage.dueDate), "MMM d")}
|
||||
</p>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ export function CapacityCell({
|
|||
>
|
||||
<span>{primary}</span>
|
||||
{count > 0 && bookedHours > 0 && (
|
||||
<span className="text-[9px] font-normal text-[var(--muted-foreground)]">
|
||||
<span className="text-[14px] font-normal text-[var(--muted-foreground)]">
|
||||
+{bookedHours}h
|
||||
</span>
|
||||
)}
|
||||
|
|
@ -108,7 +108,7 @@ export function CapacityCell({
|
|||
{statusEntries.map(([status, num]) => (
|
||||
<span
|
||||
key={status}
|
||||
className="inline-flex items-center gap-1 text-[10px]"
|
||||
className="inline-flex items-center gap-1 text-[15px]"
|
||||
>
|
||||
<span
|
||||
className={cn("h-2 w-2 rounded-full", STATUS_COLORS[status] || "bg-gray-400")}
|
||||
|
|
|
|||
|
|
@ -80,11 +80,11 @@ export function CapacityDetailPopover({
|
|||
{a.deliverableName}
|
||||
</Link>
|
||||
<div className="flex items-center gap-1.5 mt-0.5">
|
||||
<p className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<p className="text-[15px] text-[var(--muted-foreground)]">
|
||||
{a.stageName}
|
||||
</p>
|
||||
<span className="text-[10px] text-[var(--muted-foreground)]">·</span>
|
||||
<p className="text-[10px] text-[var(--muted-foreground)]">
|
||||
<span className="text-[15px] text-[var(--muted-foreground)]">·</span>
|
||||
<p className="text-[15px] text-[var(--muted-foreground)]">
|
||||
{a.projectName}
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -92,14 +92,14 @@ export function CapacityDetailPopover({
|
|||
<div className="flex items-center gap-1.5 shrink-0">
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className={`text-[10px] ${PRIORITY_STYLES[a.priority] || ""}`}
|
||||
className={`text-[15px] ${PRIORITY_STYLES[a.priority] || ""}`}
|
||||
>
|
||||
{a.priority}
|
||||
</Badge>
|
||||
<StageStatusBadge status={a.stageStatus} />
|
||||
</div>
|
||||
{a.dueDate && (
|
||||
<span className="shrink-0 text-[10px] text-[var(--muted-foreground)]">
|
||||
<span className="shrink-0 text-[15px] text-[var(--muted-foreground)]">
|
||||
Due {format(new Date(a.dueDate), "MMM d")}
|
||||
</span>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -62,19 +62,19 @@ export function CapacityGrid({ users, weeks }: CapacityGridProps) {
|
|||
<table className="w-full border-collapse text-sm">
|
||||
<thead>
|
||||
<tr className="bg-[var(--muted)]/50">
|
||||
<th className="sticky left-0 z-10 min-w-[200px] bg-[var(--muted)]/50 px-3 py-2.5 text-left text-[10px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
<th className="sticky left-0 z-10 min-w-[200px] bg-[var(--muted)]/50 px-3 py-2.5 text-left text-[15px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
Team Member
|
||||
</th>
|
||||
<th className="min-w-[60px] px-1 py-2.5 text-center text-[10px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
<th className="min-w-[60px] px-1 py-2.5 text-center text-[15px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
Active
|
||||
</th>
|
||||
<th className="min-w-[60px] px-1 py-2.5 text-center text-[10px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
<th className="min-w-[60px] px-1 py-2.5 text-center text-[15px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
Cap
|
||||
</th>
|
||||
{weeks.map((week) => (
|
||||
<th
|
||||
key={week.weekStart}
|
||||
className="min-w-[64px] px-1 py-2.5 text-center text-[10px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]"
|
||||
className="min-w-[64px] px-1 py-2.5 text-center text-[15px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]"
|
||||
>
|
||||
{week.weekLabel}
|
||||
</th>
|
||||
|
|
@ -98,7 +98,7 @@ export function CapacityGrid({ users, weeks }: CapacityGridProps) {
|
|||
<div className="flex items-center gap-2.5">
|
||||
<Avatar className="h-7 w-7">
|
||||
{user.userImage && <AvatarImage src={user.userImage} />}
|
||||
<AvatarFallback className="text-[10px] font-semibold">
|
||||
<AvatarFallback className="text-[15px] font-semibold">
|
||||
{(user.userName || "?")
|
||||
.split(" ")
|
||||
.map((n) => n[0])
|
||||
|
|
@ -110,11 +110,11 @@ export function CapacityGrid({ users, weeks }: CapacityGridProps) {
|
|||
<div className="min-w-0">
|
||||
<p className="text-xs font-medium truncate">{user.userName}</p>
|
||||
<div className="flex items-center gap-1">
|
||||
<Badge variant="outline" className="h-4 text-[9px] px-1">
|
||||
<Badge variant="outline" className="h-4 text-[14px] px-1">
|
||||
{user.role}
|
||||
</Badge>
|
||||
{user.department && (
|
||||
<span className="text-[9px] text-[var(--muted-foreground)] truncate">
|
||||
<span className="text-[14px] text-[var(--muted-foreground)] truncate">
|
||||
{user.department}
|
||||
</span>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -101,18 +101,18 @@ export function UtilizationHeatmap({
|
|||
<table className="w-full border-collapse">
|
||||
<thead>
|
||||
<tr className="bg-[var(--muted)]/50">
|
||||
<th className="sticky left-0 z-10 min-w-[160px] bg-[var(--muted)]/50 px-3 py-2.5 text-left text-[10px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
<th className="sticky left-0 z-10 min-w-[160px] bg-[var(--muted)]/50 px-3 py-2.5 text-left text-[15px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
Team Member
|
||||
</th>
|
||||
{weeks.map((week) => (
|
||||
<th
|
||||
key={week.weekStart}
|
||||
className="min-w-[56px] px-0.5 py-2.5 text-center text-[10px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]"
|
||||
className="min-w-[56px] px-0.5 py-2.5 text-center text-[15px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]"
|
||||
>
|
||||
{week.weekLabel}
|
||||
</th>
|
||||
))}
|
||||
<th className="min-w-[60px] px-2 py-2.5 text-center text-[10px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
<th className="min-w-[60px] px-2 py-2.5 text-center text-[15px] font-semibold uppercase tracking-wider text-[var(--muted-foreground)]">
|
||||
Avg
|
||||
</th>
|
||||
</tr>
|
||||
|
|
@ -128,7 +128,7 @@ export function UtilizationHeatmap({
|
|||
<tr key={user.userId} className="border-t">
|
||||
<td className="sticky left-0 z-10 bg-[var(--card)] px-3 py-1.5">
|
||||
<p className="text-xs font-medium truncate">{user.userName}</p>
|
||||
<p className="text-[9px] text-[var(--muted-foreground)]">
|
||||
<p className="text-[14px] text-[var(--muted-foreground)]">
|
||||
Cap: {user.maxCapacity}
|
||||
</p>
|
||||
</td>
|
||||
|
|
@ -144,7 +144,7 @@ export function UtilizationHeatmap({
|
|||
<TooltipTrigger asChild>
|
||||
<div
|
||||
className={cn(
|
||||
"flex h-9 items-center justify-center rounded text-[10px] font-medium",
|
||||
"flex h-9 items-center justify-center rounded text-[15px] font-medium",
|
||||
getHeatColor(utilization),
|
||||
getTextColor(utilization)
|
||||
)}
|
||||
|
|
@ -173,7 +173,7 @@ export function UtilizationHeatmap({
|
|||
<td className="px-1 py-1">
|
||||
<div
|
||||
className={cn(
|
||||
"flex h-9 items-center justify-center rounded text-[10px] font-bold",
|
||||
"flex h-9 items-center justify-center rounded text-[15px] font-bold",
|
||||
getHeatColor(avgUtilization),
|
||||
getTextColor(avgUtilization)
|
||||
)}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue