feat(frontend): add React UI — procurement office, wizard, suppliers, inventory panel (dash port), role-based login
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
import type { Milestone } from "../../lib/types";
|
||||
import { TermsheetCard } from "../../components/TermsheetCard";
|
||||
import { itemRowLabel, type ItemDraft } from "./Step2LineItems";
|
||||
|
||||
export interface TsCardDraft {
|
||||
key: string;
|
||||
milestones: Milestone[];
|
||||
}
|
||||
|
||||
export interface Step3Props {
|
||||
cards: TsCardDraft[];
|
||||
onCardsChange: (cards: TsCardDraft[]) => void;
|
||||
items: ItemDraft[];
|
||||
coverage: Record<string, string[]>; // cardKey → tempIdهای تیکخورده
|
||||
onCoverageChange: (cardKey: string, ids: string[]) => void;
|
||||
}
|
||||
|
||||
// پوشش مؤثر هر کارت: تیکهای صریح، یا (کارت اول بدون تیک) همهی اقلام
|
||||
export function effectiveCovered(cards: TsCardDraft[], coverage: Record<string, string[]>, items: ItemDraft[], cardKey: string): string[] {
|
||||
const explicit = coverage[cardKey];
|
||||
if (explicit && explicit.length) return explicit;
|
||||
if (cards[0]?.key === cardKey) return items.map((i) => i.tempId);
|
||||
return [];
|
||||
}
|
||||
|
||||
// مرحله ۳ — شرایط پرداخت (termsheetها) با چکلیست اقلام
|
||||
export function Step3Termsheets({ cards, onCardsChange, items, coverage, onCoverageChange }: Step3Props) {
|
||||
const itemOptions = items.map((it) => ({ tempId: it.tempId, label: itemRowLabel(it) }));
|
||||
|
||||
const addCard = () => {
|
||||
onCardsChange([
|
||||
...cards,
|
||||
{ key: "new-ts-" + Date.now() + Math.random().toString(36).slice(2, 6), milestones: [{ when_iso: null, amount: null }] },
|
||||
]);
|
||||
};
|
||||
|
||||
const removeCard = (key: string) => {
|
||||
onCardsChange(cards.filter((c) => c.key !== key));
|
||||
onCoverageChange(key, []);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h3 className="mb-1 text-[15px] font-bold">
|
||||
<span className="me-1 inline-flex h-6 w-6 items-center justify-center rounded-full bg-accent text-[12px] font-bold text-white">۳</span>
|
||||
شرایط پرداخت — termsheet ها
|
||||
</h3>
|
||||
<div className="mb-3 text-[12px] text-muted" dangerouslySetInnerHTML={{ __html: 'هر <b>termsheet</b> یک روش پرداخت است (نقد، چک، اقساط...). داخل هر termsheet مشخص کنید کدام قلمها را پوشش میدهد — یک termsheet میتواند چند قلم را بگیرد، و یک قلم هم میتواند زیر چند termsheet مختلف (مثلاً هم نقد هم چک) پیشنهاد شود.' }} />
|
||||
|
||||
<div className="flex flex-col gap-3">
|
||||
{cards.map((card, idx) => (
|
||||
<TermsheetCard
|
||||
key={card.key}
|
||||
index={idx + 1}
|
||||
milestones={card.milestones}
|
||||
onMilestonesChange={(ms) => onCardsChange(cards.map((c) => (c.key === card.key ? { ...c, milestones: ms } : c)))}
|
||||
items={itemOptions}
|
||||
coveredIds={effectiveCovered(cards, coverage, items, card.key)}
|
||||
onCoverageChange={(ids) => onCoverageChange(card.key, ids)}
|
||||
onRemove={() => removeCard(card.key)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<button type="button" className="btn-line-file mt-3" onClick={addCard}>
|
||||
+ افزودن termsheet
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user