feat: improve line items layout, number inputs and supplier click-to-call

This commit is contained in:
Behrad
2026-09-22 23:06:17 +03:30
parent 972b1b49cb
commit 49dd159fb1
7 changed files with 300 additions and 197 deletions
+4 -4
View File
@@ -12,9 +12,9 @@ export interface MilestoneRowProps {
// یک ردیف مرحله‌ی پرداخت: تاریخ + مبلغ + دکمه‌ی حذف
export function MilestoneRow({ milestone, onChange, onRemove, disabled = false }: MilestoneRowProps) {
return (
<div className="flex items-start gap-3 w-full my-2">
{/* کادر انتخاب تاریخ */}
<div className="w-36 shrink-0">
<div className="flex items-start gap-2 w-full my-2">
{/* کادر انتخاب تاریخ — جمع‌وجور و دقیقاً متناسب با فرمت تاریخ */}
<div className="w-28 shrink-0">
<JalaliDatePicker
value={milestone.when_iso}
onChange={(iso) => onChange({ ...milestone, when_iso: iso })}
@@ -23,7 +23,7 @@ export function MilestoneRow({ milestone, onChange, onRemove, disabled = false }
/>
</div>
{/* کادر ورود مبلغ و حروف زیر آن */}
{/* کادر ورود مبلغ — کشیده و پرکننده حداکثری عرض صفحه */}
<div className="flex-1 min-w-0">
<NumberInput
value={milestone.amount}
+24 -18
View File
@@ -7,9 +7,11 @@ export interface NumberInputProps {
onChange: (n: number | null) => void;
placeholder?: string;
className?: string;
inputClassName?: string;
align?: "right" | "center" | "left"; // تنظیم جهت متن و اعداد (پیش‌فرض: راست‌چین)
showWords?: boolean; // نمایش معادل فارسی زیر input
inputMode?: "numeric" | "decimal" | undefined;
maxDecimals?: number; // حداکثر رقم اعشار (مثلاً ۲ برای قیمت واحد)
maxDecimals?: number; // حداکثر رقم اعشار
disabled?: boolean;
}
@@ -19,6 +21,8 @@ export function NumberInput({
onChange,
placeholder,
className,
inputClassName,
align = "right",
showWords = false,
inputMode = "numeric",
maxDecimals,
@@ -36,8 +40,6 @@ export function NumberInput({
const [text, setText] = useState<string>(() => (value != null ? formatValue(value) : ""));
// همگام‌سازی وقتی value از بیرون عوض می‌شود (مثلاً محاسبه‌ی خودکار قیمت کل).
// null از بیرون متن کاربر را پاک نمی‌کند — تا تایپ میانی مثل «0.» از بین نرود.
useEffect(() => {
const cur = parseNum(text);
if (value != null && value !== cur) {
@@ -60,19 +62,28 @@ export function NumberInput({
const n = parseNum(text);
const alignClass =
align === "center"
? "!text-center text-center"
: align === "left"
? "!text-left text-left"
: "!text-right text-right";
return (
<div className={cn("min-w-0", className)}>
<div className={cn("min-w-0 w-full", className)}>
<input
type="text"
disabled={disabled}
className="num-input h-9 w-full rounded-lg border border-line bg-panel px-3 py-1 text-sm text-ink shadow-sm transition-colors placeholder:text-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/40 disabled:cursor-not-allowed disabled:opacity-60"
className={cn(
"num-input h-9 w-full rounded-lg border border-line bg-panel px-3 py-1 text-sm text-ink shadow-xs transition-colors placeholder:text-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/40 disabled:cursor-not-allowed disabled:opacity-60",
alignClass,
inputClassName,
)}
value={text}
placeholder={placeholder}
inputMode={inputMode}
onChange={(e) => {
const raw = e.target.value;
// فرمت‌دهی خودکار هنگام تایپ — اما جداکننده‌ی اعشارِ وسط تایپ
// («0.» یا «1.») دست‌نخورده می‌ماند تا کاربر بتواند اعشار بزند
const parsed = parseNum(raw);
const trailingSep = /[.,،/٬٫]$/.test(raw);
if (parsed != null && !trailingSep) {
@@ -85,17 +96,12 @@ export function NumberInput({
}}
onBlur={() => commit(text)}
/>
{showWords && (
<span className={cn("num-pill", n == null && "empty")}>
{n != null ? (
<>
{groupCommas(n, true)} <span className="num-words">({toWords(n)})</span>
</>
) : (
""
)}
</span>
{showWords && n != null && (
<div className="mt-1 block text-right text-[11px] text-muted">
<span className="tabular-nums font-semibold text-ink/80">{groupCommas(n, true)}</span>{" "}
<span className="text-muted">({toWords(n)})</span>
</div>
)}
</div>
);
}
}