Frontend-only application of prod_v2 (7bc6845, a29eafc). Dev docker-compose.yml change (b11cb36) and backups churn excluded.
106 lines
3.5 KiB
TypeScript
106 lines
3.5 KiB
TypeScript
import type { Milestone } from "../lib/types";
|
|
import { faNum } from "../lib/formatters";
|
|
import { MilestoneRow } from "./MilestoneRow";
|
|
|
|
export interface TermsheetItem {
|
|
tempId: string;
|
|
label: string;
|
|
}
|
|
|
|
export interface TermsheetCardProps {
|
|
index: number;
|
|
milestones: Milestone[];
|
|
onMilestonesChange: (ms: Milestone[]) => void;
|
|
items?: TermsheetItem[];
|
|
coveredIds?: string[];
|
|
onCoverageChange?: (ids: string[]) => void;
|
|
onRemove?: () => void;
|
|
defaultAll?: boolean;
|
|
removable?: boolean;
|
|
}
|
|
|
|
export function TermsheetCard({
|
|
index,
|
|
milestones,
|
|
onMilestonesChange,
|
|
items = [],
|
|
coveredIds = [],
|
|
onCoverageChange,
|
|
onRemove,
|
|
defaultAll = false,
|
|
removable = true,
|
|
}: TermsheetCardProps) {
|
|
const covered = new Set(coveredIds);
|
|
const effectiveCovered = covered.size === 0 && defaultAll ? items.map((i) => i.tempId) : coveredIds;
|
|
|
|
const toggle = (tempId: string, checked: boolean) => {
|
|
if (!onCoverageChange) return;
|
|
const next = new Set(covered);
|
|
if (checked) next.add(tempId);
|
|
else next.delete(tempId);
|
|
onCoverageChange([...next]);
|
|
};
|
|
|
|
const addMilestone = () => {
|
|
onMilestonesChange([...milestones, { when_iso: null, amount: null }]);
|
|
};
|
|
|
|
return (
|
|
<div className="rounded-xl border border-line bg-panel p-4">
|
|
<div className="flex items-center justify-between mb-2">
|
|
<span className="font-bold text-[13px]">گزینه {faNum(index)}</span>
|
|
{removable && onRemove && (
|
|
<button
|
|
type="button"
|
|
className="text-gray-400 hover:text-red-500 p-1 rounded transition"
|
|
title="حذف گزینه"
|
|
onClick={onRemove}
|
|
>
|
|
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.8} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
|
|
</svg>
|
|
</button>
|
|
)}
|
|
</div>
|
|
|
|
<div className="mb-1.5 text-[12px] text-muted">مراحل پرداخت:</div>
|
|
<div className="mb-2 flex flex-col gap-1.5">
|
|
{milestones.map((m, i) => (
|
|
<MilestoneRow
|
|
key={i}
|
|
milestone={m}
|
|
onChange={(next) => onMilestonesChange(milestones.map((x, j) => (j === i ? next : x)))}
|
|
onRemove={() => onMilestonesChange(milestones.filter((_, j) => j !== i))}
|
|
/>
|
|
))}
|
|
</div>
|
|
|
|
<button
|
|
type="button"
|
|
className="w-full py-1.5 border border-dashed border-gray-300 hover:border-blue-500 hover:text-blue-600 rounded-lg text-[12px] text-gray-500 transition mb-3"
|
|
onClick={addMilestone}
|
|
>
|
|
+ مرحله پرداخت
|
|
</button>
|
|
|
|
{items.length > 0 && (
|
|
<div className="pt-2 border-t border-line/60">
|
|
<div className="text-[12px] text-muted mb-1.5">این termsheet کدام اقلام (مرحله ۲) را پوشش میدهد؟</div>
|
|
<div className="flex flex-col gap-1 text-[12px]">
|
|
{items.map((it) => (
|
|
<label key={it.tempId} className="flex items-center gap-2 cursor-pointer text-gray-700">
|
|
<input
|
|
type="checkbox"
|
|
className="rounded border-gray-300"
|
|
checked={effectiveCovered.includes(it.tempId)}
|
|
onChange={(e) => toggle(it.tempId, e.target.checked)}
|
|
/>
|
|
<span>{it.label}</span>
|
|
</label>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
} |