refactor(web): remove unnecessary "use client" from 6 pure-render components
BenchResourceCard, MobileProjectCard, MobileCapacityCard, DynamicFieldRenderer, BudgetStatusBar, and TimelineHeader use no hooks, event handlers, or browser APIs — they can be server components, reducing client bundle size. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,3 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
||||||
interface BenchResourceCardProps {
|
interface BenchResourceCardProps {
|
||||||
@@ -29,11 +27,7 @@ export function BenchResourceCard({
|
|||||||
.join("");
|
.join("");
|
||||||
|
|
||||||
const availabilityLevel =
|
const availabilityLevel =
|
||||||
availableHoursPerDay >= 6
|
availableHoursPerDay >= 6 ? "high" : availableHoursPerDay >= 3 ? "medium" : "low";
|
||||||
? "high"
|
|
||||||
: availableHoursPerDay >= 3
|
|
||||||
? "medium"
|
|
||||||
: "low";
|
|
||||||
|
|
||||||
const levelClass =
|
const levelClass =
|
||||||
availabilityLevel === "high"
|
availabilityLevel === "high"
|
||||||
@@ -55,10 +49,14 @@ export function BenchResourceCard({
|
|||||||
<div className={`rounded-xl border p-4 space-y-3 ${levelClass}`}>
|
<div className={`rounded-xl border p-4 space-y-3 ${levelClass}`}>
|
||||||
<div className="flex items-start gap-3">
|
<div className="flex items-start gap-3">
|
||||||
<div className="h-10 w-10 shrink-0 rounded-full bg-brand-100 dark:bg-brand-900/40 flex items-center justify-center">
|
<div className="h-10 w-10 shrink-0 rounded-full bg-brand-100 dark:bg-brand-900/40 flex items-center justify-center">
|
||||||
<span className="text-sm font-semibold text-brand-700 dark:text-brand-300">{initials}</span>
|
<span className="text-sm font-semibold text-brand-700 dark:text-brand-300">
|
||||||
|
{initials}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="min-w-0 flex-1">
|
<div className="min-w-0 flex-1">
|
||||||
<div className="font-medium text-sm text-gray-900 dark:text-gray-100 truncate">{name}</div>
|
<div className="font-medium text-sm text-gray-900 dark:text-gray-100 truncate">
|
||||||
|
{name}
|
||||||
|
</div>
|
||||||
<div className="text-xs text-gray-500 dark:text-gray-400">{eid}</div>
|
<div className="text-xs text-gray-500 dark:text-gray-400">{eid}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import { clsx } from "clsx";
|
import { clsx } from "clsx";
|
||||||
import { formatDateLong } from "~/lib/format.js";
|
import { formatDateLong } from "~/lib/format.js";
|
||||||
import { FieldType } from "@capakraken/shared";
|
import { FieldType } from "@capakraken/shared";
|
||||||
@@ -36,9 +34,7 @@ function renderValue(fieldDef: BlueprintFieldDefinition, value: unknown): React.
|
|||||||
<span
|
<span
|
||||||
className={clsx(
|
className={clsx(
|
||||||
"inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium",
|
"inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium",
|
||||||
bool
|
bool ? "bg-green-100 text-green-700" : "bg-gray-100 text-gray-500",
|
||||||
? "bg-green-100 text-green-700"
|
|
||||||
: "bg-gray-100 text-gray-500",
|
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{bool ? "Yes" : "No"}
|
{bool ? "Yes" : "No"}
|
||||||
@@ -100,9 +96,7 @@ function FieldRow({ fieldDef, value }: { fieldDef: BlueprintFieldDefinition; val
|
|||||||
{fieldDef.label}
|
{fieldDef.label}
|
||||||
</dt>
|
</dt>
|
||||||
<dd className="text-sm">{renderValue(fieldDef, value)}</dd>
|
<dd className="text-sm">{renderValue(fieldDef, value)}</dd>
|
||||||
{fieldDef.description && (
|
{fieldDef.description && <p className="text-xs text-gray-400">{fieldDef.description}</p>}
|
||||||
<p className="text-xs text-gray-400">{fieldDef.description}</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
interface MobileCapacityCardProps {
|
interface MobileCapacityCardProps {
|
||||||
totalResources: number;
|
totalResources: number;
|
||||||
activeResources: number;
|
activeResources: number;
|
||||||
@@ -16,8 +14,7 @@ export function MobileCapacityCard({
|
|||||||
const pct = Math.min(100, Math.max(0, avgUtilizationPct));
|
const pct = Math.min(100, Math.max(0, avgUtilizationPct));
|
||||||
const circumference = 2 * Math.PI * 34; // radius = 34
|
const circumference = 2 * Math.PI * 34; // radius = 34
|
||||||
const dashOffset = circumference * (1 - pct / 100);
|
const dashOffset = circumference * (1 - pct / 100);
|
||||||
const color =
|
const color = pct >= 90 ? "#d97706" : pct >= 70 ? "#059669" : "#6b7280";
|
||||||
pct >= 90 ? "#d97706" : pct >= 70 ? "#059669" : "#6b7280";
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="rounded-2xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900 p-5">
|
<div className="rounded-2xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900 p-5">
|
||||||
@@ -27,7 +24,15 @@ export function MobileCapacityCard({
|
|||||||
<div className="flex items-center gap-5">
|
<div className="flex items-center gap-5">
|
||||||
{/* CSS-only donut */}
|
{/* CSS-only donut */}
|
||||||
<svg width="80" height="80" viewBox="0 0 80 80" className="shrink-0">
|
<svg width="80" height="80" viewBox="0 0 80 80" className="shrink-0">
|
||||||
<circle cx="40" cy="40" r="34" fill="none" stroke="#e5e7eb" strokeWidth="8" className="dark:stroke-gray-700" />
|
<circle
|
||||||
|
cx="40"
|
||||||
|
cy="40"
|
||||||
|
r="34"
|
||||||
|
fill="none"
|
||||||
|
stroke="#e5e7eb"
|
||||||
|
strokeWidth="8"
|
||||||
|
className="dark:stroke-gray-700"
|
||||||
|
/>
|
||||||
<circle
|
<circle
|
||||||
cx="40"
|
cx="40"
|
||||||
cy="40"
|
cy="40"
|
||||||
@@ -40,7 +45,15 @@ export function MobileCapacityCard({
|
|||||||
strokeLinecap="round"
|
strokeLinecap="round"
|
||||||
transform="rotate(-90 40 40)"
|
transform="rotate(-90 40 40)"
|
||||||
/>
|
/>
|
||||||
<text x="40" y="40" textAnchor="middle" dominantBaseline="middle" fontSize="15" fontWeight="700" fill={color}>
|
<text
|
||||||
|
x="40"
|
||||||
|
y="40"
|
||||||
|
textAnchor="middle"
|
||||||
|
dominantBaseline="middle"
|
||||||
|
fontSize="15"
|
||||||
|
fontWeight="700"
|
||||||
|
fill={color}
|
||||||
|
>
|
||||||
{Math.round(pct)}%
|
{Math.round(pct)}%
|
||||||
</text>
|
</text>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -54,7 +67,9 @@ export function MobileCapacityCard({
|
|||||||
{overbookedCount > 0 && (
|
{overbookedCount > 0 && (
|
||||||
<div className="flex items-center justify-between text-sm">
|
<div className="flex items-center justify-between text-sm">
|
||||||
<span className="text-amber-600 dark:text-amber-400">Overbooked</span>
|
<span className="text-amber-600 dark:text-amber-400">Overbooked</span>
|
||||||
<span className="font-semibold text-amber-600 dark:text-amber-400">{overbookedCount}</span>
|
<span className="font-semibold text-amber-600 dark:text-amber-400">
|
||||||
|
{overbookedCount}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
||||||
const STATUS_BADGE: Record<string, string> = {
|
const STATUS_BADGE: Record<string, string> = {
|
||||||
ACTIVE: "bg-emerald-100 text-emerald-800 dark:bg-emerald-900/40 dark:text-emerald-300",
|
ACTIVE: "bg-emerald-100 text-emerald-800 dark:bg-emerald-900/40 dark:text-emerald-300",
|
||||||
DRAFT: "bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-400",
|
DRAFT: "bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-400",
|
||||||
ON_HOLD: "bg-amber-100 text-amber-800 dark:bg-amber-900/40 dark:text-amber-300",
|
ON_HOLD: "bg-amber-100 text-amber-800 dark:bg-amber-900/40 dark:text-amber-300",
|
||||||
COMPLETED: "bg-blue-100 text-blue-800 dark:bg-blue-900/40 dark:text-blue-300",
|
COMPLETED: "bg-blue-100 text-blue-800 dark:bg-blue-900/40 dark:text-blue-300",
|
||||||
CANCELLED: "bg-red-100 text-red-700 dark:bg-red-900/40 dark:text-red-300",
|
CANCELLED: "bg-red-100 text-red-700 dark:bg-red-900/40 dark:text-red-300",
|
||||||
};
|
};
|
||||||
@@ -18,20 +16,32 @@ interface MobileProjectCardProps {
|
|||||||
allocationsCount?: number;
|
allocationsCount?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function MobileProjectCard({ id, shortCode, name, status, allocationsCount }: MobileProjectCardProps) {
|
export function MobileProjectCard({
|
||||||
|
id,
|
||||||
|
shortCode,
|
||||||
|
name,
|
||||||
|
status,
|
||||||
|
allocationsCount,
|
||||||
|
}: MobileProjectCardProps) {
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
href={`/projects/${id}`}
|
href={`/projects/${id}`}
|
||||||
className="flex items-center gap-3 rounded-xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900 px-4 py-3 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
|
className="flex items-center gap-3 rounded-xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900 px-4 py-3 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
|
||||||
>
|
>
|
||||||
<div className="font-mono text-xs text-gray-500 dark:text-gray-400 w-16 shrink-0">{shortCode}</div>
|
<div className="font-mono text-xs text-gray-500 dark:text-gray-400 w-16 shrink-0">
|
||||||
|
{shortCode}
|
||||||
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="text-sm font-medium text-gray-900 dark:text-gray-100 truncate">{name}</div>
|
<div className="text-sm font-medium text-gray-900 dark:text-gray-100 truncate">{name}</div>
|
||||||
{allocationsCount !== undefined && (
|
{allocationsCount !== undefined && (
|
||||||
<div className="text-xs text-gray-500 dark:text-gray-400">{allocationsCount} allocation{allocationsCount !== 1 ? "s" : ""}</div>
|
<div className="text-xs text-gray-500 dark:text-gray-400">
|
||||||
|
{allocationsCount} allocation{allocationsCount !== 1 ? "s" : ""}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<span className={`shrink-0 rounded-full px-2 py-0.5 text-[11px] font-medium ${STATUS_BADGE[status] ?? STATUS_BADGE["DRAFT"]}`}>
|
<span
|
||||||
|
className={`shrink-0 rounded-full px-2 py-0.5 text-[11px] font-medium ${STATUS_BADGE[status] ?? STATUS_BADGE["DRAFT"]}`}
|
||||||
|
>
|
||||||
{status.charAt(0) + status.slice(1).toLowerCase().replace("_", " ")}
|
{status.charAt(0) + status.slice(1).toLowerCase().replace("_", " ")}
|
||||||
</span>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import { clsx } from "clsx";
|
import { clsx } from "clsx";
|
||||||
import { formatMoney } from "~/lib/format.js";
|
import { formatMoney } from "~/lib/format.js";
|
||||||
import { InfoTooltip } from "~/components/ui/InfoTooltip.js";
|
import { InfoTooltip } from "~/components/ui/InfoTooltip.js";
|
||||||
@@ -55,14 +53,18 @@ export function BudgetStatusBar({
|
|||||||
|
|
||||||
// Cap visual bar segments at 100% total
|
// Cap visual bar segments at 100% total
|
||||||
const cappedConfirmedPercent = Math.min(confirmedPercent, 100);
|
const cappedConfirmedPercent = Math.min(confirmedPercent, 100);
|
||||||
const cappedProposedPercent = Math.min(proposedPercent, Math.max(0, 100 - cappedConfirmedPercent));
|
const cappedProposedPercent = Math.min(
|
||||||
|
proposedPercent,
|
||||||
|
Math.max(0, 100 - cappedConfirmedPercent),
|
||||||
|
);
|
||||||
|
|
||||||
const highestWarning = warnings.length > 0
|
const highestWarning =
|
||||||
? warnings.reduce((prev, curr) => {
|
warnings.length > 0
|
||||||
const levels: Record<string, number> = { info: 0, warning: 1, critical: 2 };
|
? warnings.reduce((prev, curr) => {
|
||||||
return (levels[curr.level] ?? 0) > (levels[prev.level] ?? 0) ? curr : prev;
|
const levels: Record<string, number> = { info: 0, warning: 1, critical: 2 };
|
||||||
})
|
return (levels[curr.level] ?? 0) > (levels[prev.level] ?? 0) ? curr : prev;
|
||||||
: null;
|
})
|
||||||
|
: null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={clsx("space-y-1.5", className)}>
|
<div className={clsx("space-y-1.5", className)}>
|
||||||
@@ -74,12 +76,18 @@ export function BudgetStatusBar({
|
|||||||
<div className="relative h-3 bg-gray-100 rounded-full overflow-hidden">
|
<div className="relative h-3 bg-gray-100 rounded-full overflow-hidden">
|
||||||
{/* Confirmed segment */}
|
{/* Confirmed segment */}
|
||||||
<div
|
<div
|
||||||
className={clsx("absolute left-0 top-0 h-full transition-all", getConfirmedBarColor(utilizationPercent))}
|
className={clsx(
|
||||||
|
"absolute left-0 top-0 h-full transition-all",
|
||||||
|
getConfirmedBarColor(utilizationPercent),
|
||||||
|
)}
|
||||||
style={{ width: `${cappedConfirmedPercent}%` }}
|
style={{ width: `${cappedConfirmedPercent}%` }}
|
||||||
/>
|
/>
|
||||||
{/* Proposed segment */}
|
{/* Proposed segment */}
|
||||||
<div
|
<div
|
||||||
className={clsx("absolute top-0 h-full transition-all", getProposedBarColor(utilizationPercent))}
|
className={clsx(
|
||||||
|
"absolute top-0 h-full transition-all",
|
||||||
|
getProposedBarColor(utilizationPercent),
|
||||||
|
)}
|
||||||
style={{ left: `${cappedConfirmedPercent}%`, width: `${cappedProposedPercent}%` }}
|
style={{ left: `${cappedConfirmedPercent}%`, width: `${cappedProposedPercent}%` }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -89,8 +97,7 @@ export function BudgetStatusBar({
|
|||||||
<span>
|
<span>
|
||||||
<span className="font-medium">{formatEur(allocatedCents)}</span>
|
<span className="font-medium">{formatEur(allocatedCents)}</span>
|
||||||
{" / "}
|
{" / "}
|
||||||
<span>{formatEur(budgetCents)}</span>
|
<span>{formatEur(budgetCents)}</span>{" "}
|
||||||
{" "}
|
|
||||||
<span className="text-gray-400">({utilizationPercent.toFixed(1)}%)</span>
|
<span className="text-gray-400">({utilizationPercent.toFixed(1)}%)</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@@ -102,12 +109,20 @@ export function BudgetStatusBar({
|
|||||||
getWarningBadgeStyle(highestWarning.level),
|
getWarningBadgeStyle(highestWarning.level),
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{highestWarning.level === "critical" ? "⚠" : highestWarning.level === "warning" ? "!" : "i"}
|
{highestWarning.level === "critical"
|
||||||
|
? "⚠"
|
||||||
|
: highestWarning.level === "warning"
|
||||||
|
? "!"
|
||||||
|
: "i"}
|
||||||
{warnings.length > 1 ? `${warnings.length} warnings` : "Warning"}
|
{warnings.length > 1 ? `${warnings.length} warnings` : "Warning"}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
<span className={clsx("font-medium", remainingCents < 0 ? "text-red-600" : "text-gray-700")}>
|
<span
|
||||||
{remainingCents >= 0 ? `${formatEur(remainingCents)} left` : `${formatEur(Math.abs(remainingCents))} over`}
|
className={clsx("font-medium", remainingCents < 0 ? "text-red-600" : "text-gray-700")}
|
||||||
|
>
|
||||||
|
{remainingCents >= 0
|
||||||
|
? `${formatEur(remainingCents)} left`
|
||||||
|
: `${formatEur(Math.abs(remainingCents))} over`}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -115,11 +130,21 @@ export function BudgetStatusBar({
|
|||||||
{/* Legend */}
|
{/* Legend */}
|
||||||
<div className="flex items-center gap-3 text-xs text-gray-500">
|
<div className="flex items-center gap-3 text-xs text-gray-500">
|
||||||
<span className="flex items-center gap-1">
|
<span className="flex items-center gap-1">
|
||||||
<span className={clsx("inline-block w-2.5 h-2.5 rounded-sm", getConfirmedBarColor(utilizationPercent))} />
|
<span
|
||||||
|
className={clsx(
|
||||||
|
"inline-block w-2.5 h-2.5 rounded-sm",
|
||||||
|
getConfirmedBarColor(utilizationPercent),
|
||||||
|
)}
|
||||||
|
/>
|
||||||
Confirmed {formatEur(confirmedCents)}
|
Confirmed {formatEur(confirmedCents)}
|
||||||
</span>
|
</span>
|
||||||
<span className="flex items-center gap-1">
|
<span className="flex items-center gap-1">
|
||||||
<span className={clsx("inline-block w-2.5 h-2.5 rounded-sm", getProposedBarColor(utilizationPercent))} />
|
<span
|
||||||
|
className={clsx(
|
||||||
|
"inline-block w-2.5 h-2.5 rounded-sm",
|
||||||
|
getProposedBarColor(utilizationPercent),
|
||||||
|
)}
|
||||||
|
/>
|
||||||
Proposed {formatEur(proposedCents)}
|
Proposed {formatEur(proposedCents)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import { clsx } from "clsx";
|
import { clsx } from "clsx";
|
||||||
import { MONTHS_SHORT } from "./timelineConstants.js";
|
import { MONTHS_SHORT } from "./timelineConstants.js";
|
||||||
|
|
||||||
@@ -33,7 +31,10 @@ export function TimelineHeader({
|
|||||||
className="sticky top-0 z-40 flex bg-white dark:bg-gray-900 border-b border-gray-100 dark:border-gray-800"
|
className="sticky top-0 z-40 flex bg-white dark:bg-gray-900 border-b border-gray-100 dark:border-gray-800"
|
||||||
style={{ height: HEADER_MONTH_HEIGHT }}
|
style={{ height: HEADER_MONTH_HEIGHT }}
|
||||||
>
|
>
|
||||||
<div className="flex-shrink-0 border-r border-gray-200 dark:border-gray-700" style={{ width: LABEL_WIDTH }} />
|
<div
|
||||||
|
className="flex-shrink-0 border-r border-gray-200 dark:border-gray-700"
|
||||||
|
style={{ width: LABEL_WIDTH }}
|
||||||
|
/>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
{monthGroups.map((m, i) => (
|
{monthGroups.map((m, i) => (
|
||||||
<div
|
<div
|
||||||
@@ -72,27 +73,41 @@ export function TimelineHeader({
|
|||||||
key={i}
|
key={i}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
"flex-shrink-0 border-r flex flex-col items-center justify-center text-xs overflow-hidden",
|
"flex-shrink-0 border-r flex flex-col items-center justify-center text-xs overflow-hidden",
|
||||||
isToday ? "bg-brand-50 dark:bg-brand-950/40 border-brand-200 dark:border-brand-800" :
|
isToday
|
||||||
isWeekend ? "bg-brand-50/60 dark:bg-brand-950/30 border-brand-200 dark:border-brand-800" :
|
? "bg-brand-50 dark:bg-brand-950/40 border-brand-200 dark:border-brand-800"
|
||||||
isMonday ? "border-gray-200 dark:border-gray-700" : "border-gray-100 dark:border-gray-800",
|
: isWeekend
|
||||||
|
? "bg-brand-50/60 dark:bg-brand-950/30 border-brand-200 dark:border-brand-800"
|
||||||
|
: isMonday
|
||||||
|
? "border-gray-200 dark:border-gray-700"
|
||||||
|
: "border-gray-100 dark:border-gray-800",
|
||||||
)}
|
)}
|
||||||
style={{ width: CELL_WIDTH, height: HEADER_DAY_HEIGHT }}
|
style={{ width: CELL_WIDTH, height: HEADER_DAY_HEIGHT }}
|
||||||
>
|
>
|
||||||
{showLabel && (
|
{showLabel && (
|
||||||
<>
|
<>
|
||||||
<span className={clsx(
|
<span
|
||||||
"font-medium leading-none",
|
className={clsx(
|
||||||
isToday ? "text-brand-600" : isWeekend ? "text-brand-600 dark:text-brand-400" : "text-gray-600 dark:text-gray-300",
|
"font-medium leading-none",
|
||||||
)}>
|
isToday
|
||||||
|
? "text-brand-600"
|
||||||
|
: isWeekend
|
||||||
|
? "text-brand-600 dark:text-brand-400"
|
||||||
|
: "text-gray-600 dark:text-gray-300",
|
||||||
|
)}
|
||||||
|
>
|
||||||
{zoom === "week"
|
{zoom === "week"
|
||||||
? `${date.getDate()} ${MONTHS_SHORT[date.getMonth()]}`
|
? `${date.getDate()} ${MONTHS_SHORT[date.getMonth()]}`
|
||||||
: date.getDate()}
|
: date.getDate()}
|
||||||
</span>
|
</span>
|
||||||
{zoom === "day" && (
|
{zoom === "day" && (
|
||||||
<span className={clsx(
|
<span
|
||||||
"text-[9px] leading-none mt-0.5",
|
className={clsx(
|
||||||
isWeekend ? "text-brand-400 dark:text-brand-500" : "text-gray-300 dark:text-gray-600",
|
"text-[9px] leading-none mt-0.5",
|
||||||
)}>
|
isWeekend
|
||||||
|
? "text-brand-400 dark:text-brand-500"
|
||||||
|
: "text-gray-300 dark:text-gray-600",
|
||||||
|
)}
|
||||||
|
>
|
||||||
{["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"][dow]}
|
{["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"][dow]}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user