"use client"; import { useState } from "react"; import { clsx } from "clsx"; import { trpc } from "~/lib/trpc/client.js"; interface ApplyEffortRulesProps { estimateId: string; canEdit: boolean; onApplied?: () => void; } export function ApplyEffortRules({ estimateId, canEdit, onApplied }: ApplyEffortRulesProps) { const utils = trpc.useUtils(); const { data: ruleSets, isLoading } = trpc.effortRule.list.useQuery(); const [selectedRuleSetId, setSelectedRuleSetId] = useState(""); const [mode, setMode] = useState<"replace" | "append">("replace"); const [showPreview, setShowPreview] = useState(false); const previewQuery = trpc.effortRule.preview.useQuery( { estimateId, ruleSetId: selectedRuleSetId }, { enabled: showPreview && Boolean(selectedRuleSetId) }, ); const applyMutation = trpc.effortRule.apply.useMutation({ onSuccess: (result) => { utils.estimate.getById.invalidate(); setShowPreview(false); onApplied?.(); if (result.warnings.length > 0) { alert(`Generated ${result.linesGenerated} demand lines.\n\nWarnings:\n${result.warnings.join("\n")}`); } }, }); // Auto-select default rule set if (!selectedRuleSetId && ruleSets) { const defaultSet = ruleSets.find((rs) => rs.isDefault) ?? ruleSets[0]; if (defaultSet) { setSelectedRuleSetId(defaultSet.id); } } if (!canEdit) return null; if (isLoading) { return

Loading effort rules...

; } if (!ruleSets || ruleSets.length === 0) { return (
No effort rule sets defined.{" "} Create one {" "} to auto-generate demand lines from scope items.
); } return (

Generate demand lines from scope

{applyMutation.error && (

{applyMutation.error.message}

)} {/* Preview */} {showPreview && previewQuery.data && (
{previewQuery.data.scopeItemCount} scope items {previewQuery.data.ruleCount} rules {previewQuery.data.lines.length} demand lines would be generated {previewQuery.data.unmatchedScopeItems.length > 0 && ( {previewQuery.data.unmatchedScopeItems.length} unmatched scope items )}
{previewQuery.data.warnings.length > 0 && (
{previewQuery.data.warnings.map((w, i) => (

{w}

))}
)} {/* Aggregated discipline summary */} {previewQuery.data.aggregated.length > 0 && (
{previewQuery.data.aggregated.map((agg, i) => ( ))}
Discipline Chapter Total hours Lines
{agg.discipline} {agg.chapter ?? "\u2014"} {agg.totalHours.toFixed(1)} h {agg.lineCount}
)} {/* Detailed lines (collapsible) */} {previewQuery.data.lines.length > 0 && (
Show all {previewQuery.data.lines.length} generated lines
{previewQuery.data.lines.map((line, i) => ( ))}
Scope item Discipline Chapter Mode Units h/unit Hours
{line.scopeItemName} {line.discipline} {line.chapter ?? "\u2014"} {line.unitMode} {line.unitCount} {line.hoursPerUnit} {line.hours.toFixed(1)}
)}
)} {showPreview && previewQuery.isLoading && (

Computing preview...

)}
); }