summaryrefslogtreecommitdiff
path: root/makima/frontend/src/components/contracts/PhaseDeliverablesPanel.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'makima/frontend/src/components/contracts/PhaseDeliverablesPanel.tsx')
-rw-r--r--makima/frontend/src/components/contracts/PhaseDeliverablesPanel.tsx339
1 files changed, 0 insertions, 339 deletions
diff --git a/makima/frontend/src/components/contracts/PhaseDeliverablesPanel.tsx b/makima/frontend/src/components/contracts/PhaseDeliverablesPanel.tsx
deleted file mode 100644
index b2c2e58..0000000
--- a/makima/frontend/src/components/contracts/PhaseDeliverablesPanel.tsx
+++ /dev/null
@@ -1,339 +0,0 @@
-import { useMemo } from "react";
-import type { ContractWithRelations, ContractPhase, ContractType } from "../../lib/api";
-
-// Phase deliverables configuration (mirrors backend phase_guidance.rs)
-// IDs must match backend phase_guidance.rs exactly for mark_deliverable_complete
-interface PhaseDeliverable {
- id: string; // Must match backend deliverable ID
- name: string;
- priority: "required" | "recommended" | "optional";
- description: string;
-}
-
-interface PhaseConfig {
- deliverables: PhaseDeliverable[];
- requiresRepository: boolean;
- requiresTasks: boolean;
- guidance: string;
-}
-
-// Contract type specific deliverables (must match backend phase_guidance.rs)
-type ContractTypeDeliverables = Partial<Record<ContractPhase, PhaseConfig>>;
-
-const CONTRACT_TYPE_DELIVERABLES: Record<ContractType, ContractTypeDeliverables> = {
- simple: {
- plan: {
- deliverables: [
- { id: "plan-document", name: "Plan", priority: "required", description: "Implementation plan detailing the approach and tasks" },
- ],
- requiresRepository: true,
- requiresTasks: false,
- guidance: "Create a plan document that outlines the implementation approach. A repository must be configured before moving to Execute phase.",
- },
- execute: {
- deliverables: [
- { id: "pull-request", name: "Pull Request", priority: "required", description: "Pull request with the implemented changes" },
- ],
- requiresRepository: true,
- requiresTasks: true,
- guidance: "Execute the plan and create a PR with the implemented changes. Complete all tasks to finish the contract.",
- },
- },
- specification: {
- research: {
- deliverables: [
- { id: "research-notes", name: "Research Notes", priority: "required", description: "Document findings and insights during research" },
- ],
- requiresRepository: false,
- requiresTasks: false,
- guidance: "Focus on understanding the problem space and document your findings in the Research Notes before moving to Specify phase.",
- },
- specify: {
- deliverables: [
- { id: "requirements-document", name: "Requirements Document", priority: "required", description: "Define functional and non-functional requirements" },
- ],
- requiresRepository: false,
- requiresTasks: false,
- guidance: "Define what needs to be built with clear requirements in the Requirements Document. Ensure specifications are detailed enough for planning.",
- },
- plan: {
- deliverables: [
- { id: "plan-document", name: "Plan", priority: "required", description: "Implementation plan detailing the approach and tasks" },
- ],
- requiresRepository: true,
- requiresTasks: false,
- guidance: "Create a plan document that outlines the implementation approach. A repository must be configured before moving to Execute phase.",
- },
- execute: {
- deliverables: [
- { id: "pull-request", name: "Pull Request", priority: "required", description: "Pull request with the implemented changes" },
- ],
- requiresRepository: true,
- requiresTasks: true,
- guidance: "Execute the plan and create a PR with the implemented changes. Complete all tasks before moving to Review phase.",
- },
- review: {
- deliverables: [
- { id: "release-notes", name: "Release Notes", priority: "required", description: "Document changes for release communication" },
- ],
- requiresRepository: false,
- requiresTasks: false,
- guidance: "Review completed work and document the release in the Release Notes. The contract can be completed after review.",
- },
- },
- execute: {
- execute: {
- deliverables: [], // No deliverables for execute-only contract type
- requiresRepository: true,
- requiresTasks: true,
- guidance: "Execute the tasks directly. No deliverable documents are required for this contract type.",
- },
- },
-};
-
-// Get phase config for a specific contract type and phase
-function getPhaseConfig(contractType: ContractType, phase: ContractPhase): PhaseConfig {
- const typeConfig = CONTRACT_TYPE_DELIVERABLES[contractType];
- const phaseConfig = typeConfig?.[phase];
-
- if (phaseConfig) {
- return phaseConfig;
- }
-
- // Fallback for unknown phase/type combinations
- return {
- deliverables: [],
- requiresRepository: false,
- requiresTasks: false,
- guidance: `Unknown phase "${phase}" for contract type "${contractType}"`,
- };
-}
-
-interface DeliverableStatus {
- id: string;
- name: string;
- priority: "required" | "recommended" | "optional";
- description: string;
- completed: boolean;
- fileId?: string;
- actualName?: string;
-}
-
-interface PhaseDeliverablesProps {
- contract: ContractWithRelations;
- onCreateFile?: (templateId: string, suggestedName: string) => void;
-}
-
-export function PhaseDeliverablesPanel({ contract, onCreateFile }: PhaseDeliverablesProps) {
- // Get phase config based on contract type AND phase
- const phaseConfig = useMemo(
- () => getPhaseConfig(contract.contractType, contract.phase),
- [contract.contractType, contract.phase]
- );
-
- // Calculate deliverable status
- const deliverableStatuses = useMemo((): DeliverableStatus[] => {
- return phaseConfig.deliverables.map((deliverable) => {
- // Find matching file by name similarity
- const matchedFile = contract.files.find((f) => {
- const nameLower = f.name.toLowerCase();
- const deliverableLower = deliverable.name.toLowerCase();
- return (
- f.contractPhase === contract.phase &&
- (nameLower.includes(deliverableLower) || deliverableLower.includes(nameLower) || nameLower.includes(deliverable.id.replace("-", " ")))
- );
- });
-
- return {
- ...deliverable,
- completed: !!matchedFile,
- fileId: matchedFile?.id,
- actualName: matchedFile?.name,
- };
- });
- }, [contract.files, contract.phase, phaseConfig.deliverables]);
-
- // Check repository status
- const hasRepository = contract.repositories.length > 0;
-
- // Check task status
- const taskStats = useMemo(() => {
- const total = contract.tasks.length;
- const done = contract.tasks.filter((t) => t.status === "done" || t.status === "merged").length;
- const pending = contract.tasks.filter((t) => t.status === "pending").length;
- const running = contract.tasks.filter((t) => ["running", "initializing", "starting"].includes(t.status)).length;
- const failed = contract.tasks.filter((t) => t.status === "failed").length;
- return { total, done, pending, running, failed };
- }, [contract.tasks]);
-
- // Calculate completion percentage
- const completionPercent = useMemo(() => {
- let completed = 0;
- let total = 0;
-
- // Count required and recommended deliverables
- deliverableStatuses.forEach((s) => {
- if (s.priority !== "optional") {
- total++;
- if (s.completed) completed++;
- }
- });
-
- // Count repository if required
- if (phaseConfig.requiresRepository) {
- total++;
- if (hasRepository) completed++;
- }
-
- // Count tasks if required
- if (phaseConfig.requiresTasks && taskStats.total > 0) {
- total++;
- if (taskStats.done === taskStats.total) completed++;
- }
-
- return total > 0 ? Math.round((completed / total) * 100) : 100;
- }, [deliverableStatuses, hasRepository, phaseConfig, taskStats]);
-
- const priorityColors = {
- required: "text-red-400",
- recommended: "text-yellow-400",
- optional: "text-[#555]",
- };
-
- return (
- <div className="space-y-4">
- <div className="flex items-center justify-between">
- <h3 className="font-mono text-xs text-[#75aafc] uppercase">
- Phase Deliverables
- </h3>
- <div className="flex items-center gap-2">
- <div className="w-24 h-1.5 bg-[rgba(117,170,252,0.1)] rounded overflow-hidden">
- <div
- className={`h-full transition-all duration-300 ${
- completionPercent === 100 ? "bg-green-400" : "bg-[#75aafc]"
- }`}
- style={{ width: `${completionPercent}%` }}
- />
- </div>
- <span className="font-mono text-[10px] text-[#555]">{completionPercent}%</span>
- </div>
- </div>
-
- {/* Guidance text */}
- <p className="font-mono text-xs text-[#555] italic">{phaseConfig.guidance}</p>
-
- {/* Deliverables checklist */}
- <div className="space-y-2">
- {deliverableStatuses.map((status) => (
- <div
- key={status.id}
- className={`flex items-center justify-between p-2 border ${
- status.completed
- ? "border-green-400/20 bg-green-400/5"
- : "border-[rgba(117,170,252,0.15)]"
- }`}
- >
- <div className="flex items-center gap-2">
- <span
- className={`font-mono text-xs ${
- status.completed ? "text-green-400" : "text-[#555]"
- }`}
- >
- {status.completed ? "[+]" : "[ ]"}
- </span>
- <div>
- <div className="flex items-center gap-2">
- <span className="font-mono text-xs text-[#dbe7ff]">
- {status.completed ? status.actualName : status.name}
- </span>
- {!status.completed && (
- <span className={`font-mono text-[9px] uppercase ${priorityColors[status.priority]}`}>
- {status.priority}
- </span>
- )}
- </div>
- <span className="font-mono text-[10px] text-[#555]">
- {status.description}
- </span>
- </div>
- </div>
- {!status.completed && onCreateFile && (
- <button
- onClick={() => onCreateFile(status.id, status.name)}
- className="px-2 py-1 font-mono text-[10px] text-[#75aafc] border border-[rgba(117,170,252,0.25)] hover:border-[#3f6fb3] transition-colors"
- >
- Create
- </button>
- )}
- </div>
- ))}
- </div>
-
- {/* Repository status */}
- {phaseConfig.requiresRepository && (
- <div
- className={`flex items-center gap-2 p-2 border ${
- hasRepository
- ? "border-green-400/20 bg-green-400/5"
- : "border-[rgba(117,170,252,0.15)]"
- }`}
- >
- <span
- className={`font-mono text-xs ${
- hasRepository ? "text-green-400" : "text-[#555]"
- }`}
- >
- {hasRepository ? "[+]" : "[ ]"}
- </span>
- <div>
- <span className="font-mono text-xs text-[#dbe7ff]">
- Repository Configured
- </span>
- {!hasRepository && (
- <span className="font-mono text-[9px] uppercase text-red-400 ml-2">
- required
- </span>
- )}
- </div>
- </div>
- )}
-
- {/* Task status */}
- {phaseConfig.requiresTasks && (
- <div
- className={`flex items-center justify-between p-2 border ${
- taskStats.total > 0 && taskStats.done === taskStats.total
- ? "border-green-400/20 bg-green-400/5"
- : "border-[rgba(117,170,252,0.15)]"
- }`}
- >
- <div className="flex items-center gap-2">
- <span
- className={`font-mono text-xs ${
- taskStats.total > 0 && taskStats.done === taskStats.total
- ? "text-green-400"
- : "text-[#555]"
- }`}
- >
- {taskStats.total > 0 && taskStats.done === taskStats.total ? "[+]" : "[ ]"}
- </span>
- <span className="font-mono text-xs text-[#dbe7ff]">
- Tasks Completed
- </span>
- </div>
- {taskStats.total > 0 ? (
- <span className="font-mono text-[10px] text-[#9bc3ff]">
- {taskStats.done}/{taskStats.total}
- {taskStats.running > 0 && ` (${taskStats.running} running)`}
- {taskStats.failed > 0 && (
- <span className="text-red-400"> ({taskStats.failed} failed)</span>
- )}
- </span>
- ) : (
- <span className="font-mono text-[10px] text-[#555]">No tasks yet</span>
- )}
- </div>
- )}
- </div>
- );
-}