"use client"; import { clsx } from "clsx"; import { memo } from "react"; import type { ShiftPreviewData } from "~/hooks/useTimelineDrag.js"; import { formatDate } from "~/lib/format.js"; import { usePermissions } from "~/hooks/usePermissions.js"; interface ShiftPreviewTooltipProps { preview: ShiftPreviewData; projectName: string; newStartDate: Date; newEndDate: Date; isLoading?: boolean; } function formatCents(cents: number): string { const abs = Math.abs(cents); const str = (abs / 100).toLocaleString("de-DE", { minimumFractionDigits: 0 }); return `${cents < 0 ? "−" : "+"}${str} €`; } export const ShiftPreviewTooltip = memo(function ShiftPreviewTooltip({ preview, projectName, newStartDate, newEndDate, isLoading, }: ShiftPreviewTooltipProps) { const { canViewCosts } = usePermissions(); const dateStr = `${formatDate(newStartDate)} → ${formatDate(newEndDate)}`; return (
{/* Header */}
{projectName}
{dateStr}
{isLoading ? (
Calculating...
) : ( <> {/* Cost delta */} {canViewCosts && preview.deltaCents !== 0 && (
Cost delta 0 ? "text-red-600" : "text-green-600", )} > {formatCents(preview.deltaCents)}
)} {/* Budget utilization */} {canViewCosts && (
Budget after 85 ? "text-yellow-600" : "text-gray-700", )} > {preview.budgetUtilizationAfter.toFixed(1)}%
)} {/* Conflicts */} {preview.conflictCount > 0 && (
⚠ {preview.conflictCount} availability conflict{preview.conflictCount > 1 ? "s" : ""}
)} {/* Errors */} {preview.errors.map((err, i) => (
✗ {err}
))} {/* Warnings */} {preview.warnings.slice(0, 2).map((warn, i) => (
{warn}
))} {/* Action hint */}
{preview.valid ? "Release to apply shift" : "Cannot apply — resolve errors first"}
)}
); });