first commit

This commit is contained in:
2026-05-31 14:22:39 +03:30
commit 98af7d639b
54 changed files with 11545 additions and 0 deletions

241
ui/forms/ReferralForm.tsx Normal file
View File

@@ -0,0 +1,241 @@
import React from "react";
import {
Box,
Paper,
TextField,
Typography,
IconButton,
Button,
MenuItem,
Divider,
} from "@mui/material";
import AddIcon from "@mui/icons-material/Add";
import { DeleteOutlineOutlined } from "@mui/icons-material";
// ---------------- Types ----------------
export type AcquaintanceType = "Direct" | "Indirect";
export interface ReferralFormData {
firstName: string;
lastName: string;
relationship: string;
acquaintanceDuration: string; // optional in DB, but keep as string
acquaintanceType: AcquaintanceType;
jobTitle: string;
workplaceName: string;
phoneNumber: string;
}
interface ReferralItemFormProps {
index: number;
value: ReferralFormData;
onChange: (next: ReferralFormData) => void;
onRemove?: () => void;
disableRemove?: boolean;
}
// ---------------- Item Form ----------------
export function ReferralItemForm({
index,
value,
onChange,
onRemove,
disableRemove,
}: ReferralItemFormProps) {
const setField =
(key: keyof ReferralFormData) =>
(e: React.ChangeEvent<HTMLInputElement>) => {
onChange({ ...value, [key]: e.target.value });
};
return (
<Paper
elevation={0}
sx={{
p: { xs: 2, md: 2.5 },
borderRadius: 2,
border: "1px solid #e5e7eb",
backgroundColor: "#fff", // بدون سبز
}}
>
<Box
sx={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
mb: 1.5,
gap: 2,
}}
>
<Typography sx={{ fontWeight: 700 }}>معرف {index + 1}</Typography>
<IconButton
onClick={onRemove}
disabled={disableRemove}
color="error"
size="small"
aria-label="حذف معرف"
>
<DeleteOutlineOutlined />
</IconButton>
</Box>
<Box
sx={{
display: "grid",
gridTemplateColumns: { xs: "1fr", md: "1fr 1fr" },
gap: 2,
}}
>
<TextField
label="نام"
value={value.firstName}
onChange={setField("firstName")}
fullWidth
required
/>
<TextField
label="نام خانوادگی"
value={value.lastName}
onChange={setField("lastName")}
fullWidth
required
/>
<TextField
label="نسبت / رابطه"
value={value.relationship}
onChange={setField("relationship")}
fullWidth
required
placeholder="مثلاً: دوست، همکار، فامیل..."
/>
<TextField
label="مدت زمان آشنایی"
value={value.acquaintanceDuration}
onChange={setField("acquaintanceDuration")}
fullWidth
placeholder="مثلاً: ۵ سال"
/>
<TextField
select
label="نوع آشنایی"
value={value.acquaintanceType}
onChange={setField("acquaintanceType")}
fullWidth
required
>
<MenuItem value="Direct">مستقیم</MenuItem>
<MenuItem value="Indirect">غیرمستقیم</MenuItem>
</TextField>
<TextField
label="تلفن تماس"
value={value.phoneNumber}
onChange={setField("phoneNumber")}
fullWidth
required
placeholder="مثلاً: 0912xxxxxxx"
/>
<TextField
label="شغل معرف"
value={value.jobTitle}
onChange={setField("jobTitle")}
fullWidth
/>
<TextField
label="نام محل کار معرف"
value={value.workplaceName}
onChange={setField("workplaceName")}
fullWidth
/>
</Box>
</Paper>
);
}
// ---------------- Section (Multi) ----------------
interface ReferralSectionProps {
value: ReferralFormData[];
onChange: (next: ReferralFormData[]) => void;
minItems?: number; // پیش‌فرض 1
maxItems?: number; // اختیاری
title?: string;
}
const emptyReferral = (): ReferralFormData => ({
firstName: "",
lastName: "",
relationship: "",
acquaintanceDuration: "",
acquaintanceType: "Direct",
jobTitle: "",
workplaceName: "",
phoneNumber: "",
});
export function ReferralSection({
value,
onChange,
minItems = 1,
maxItems,
title = "معرف‌ها",
}: ReferralSectionProps) {
const items = value?.length ? value : Array.from({ length: minItems }, emptyReferral);
const addItem = () => {
if (maxItems && items.length >= maxItems) return;
onChange([...(items || []), emptyReferral()]);
};
const updateItem = (idx: number, nextItem: ReferralFormData) => {
const next = items.map((it, i) => (i === idx ? nextItem : it));
onChange(next);
};
const removeItem = (idx: number) => {
if (items.length <= minItems) return;
const next = items.filter((_, i) => i !== idx);
onChange(next);
};
return (
<Paper
elevation={0}
>
<Box sx={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 2 }}>
<Button
onClick={addItem}
variant="contained"
startIcon={<AddIcon />}
disabled={!!maxItems && items.length >= maxItems}
>
افزودن معرف جديد
</Button>
</Box>
<Divider sx={{ my: 2 }} />
<Box sx={{ display: "grid", gap: 2 }}>
{items.map((item, idx) => (
<ReferralItemForm
key={idx}
index={idx}
value={item}
onChange={(next) => updateItem(idx, next)}
onRemove={() => removeItem(idx)}
disableRemove={items.length <= minItems}
/>
))}
</Box>
</Paper>
);
}