You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
662 lines
27 KiB
662 lines
27 KiB
import React, { useState, useEffect } from 'react'
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
DialogDescription,
|
|
DialogFooter,
|
|
DialogClose,
|
|
} from '@/components/ui/dialog'
|
|
import { Button } from '@/components/ui/button'
|
|
import { Input } from '@/components/ui/input'
|
|
import { Ic } from '@/icons'
|
|
import {
|
|
ServiceTranslation,
|
|
SERVICE_LANGUAGES,
|
|
type ServiceLanguageCode,
|
|
} from '@/components/shared/ServiceTranslation'
|
|
import type { SimCardItem, CreateSimCardInput } from '../services/simcard-api'
|
|
|
|
export type SupportedLanguage = ServiceLanguageCode
|
|
|
|
interface SimcardFormDialogProps {
|
|
open: boolean
|
|
onOpenChange: (open: boolean) => void
|
|
item?: SimCardItem | null
|
|
onSave: (data: CreateSimCardInput, id?: number) => Promise<void>
|
|
onDelete?: (id: number) => Promise<void>
|
|
}
|
|
|
|
const COUNTRIES = [
|
|
{ id: 1, code: 'IR', name: 'ایران', flag: '🇮🇷' },
|
|
{ id: 2, code: 'IQ', name: 'عراق', flag: '🇮🇶' },
|
|
{ id: 4, code: 'SA', name: 'عربستان سعودی', flag: '🇸🇦' },
|
|
]
|
|
|
|
export const LANGUAGES = SERVICE_LANGUAGES
|
|
|
|
const createEmptyTitles = (): Record<SupportedLanguage, string> => ({
|
|
ar: '',
|
|
fa: '',
|
|
en: '',
|
|
ru: '',
|
|
id: '',
|
|
})
|
|
|
|
const createEmptyDetails = (): Record<SupportedLanguage, string[]> => ({
|
|
ar: [],
|
|
fa: [],
|
|
en: [],
|
|
ru: [],
|
|
id: [],
|
|
})
|
|
|
|
export function SimcardFormDialog({
|
|
open,
|
|
onOpenChange,
|
|
item,
|
|
onSave,
|
|
onDelete,
|
|
}: SimcardFormDialogProps) {
|
|
const isEditing = Boolean(item && item.id)
|
|
|
|
const [activeLangTab, setActiveLangTab] = useState<SupportedLanguage>('ar')
|
|
const [countryId, setCountryId] = useState<number>(1)
|
|
const [price, setPrice] = useState<string>('10.00')
|
|
const [status, setStatus] = useState<number>(1)
|
|
|
|
// عناوین چندزبانه
|
|
const [titles, setTitles] = useState<Record<SupportedLanguage, string>>(createEmptyTitles)
|
|
|
|
// ویژگیهای چندزبانه
|
|
const [details, setDetails] = useState<Record<SupportedLanguage, string[]>>(createEmptyDetails)
|
|
|
|
// افزودن ویژگی جدید در زبان فعال
|
|
const [newDetailText, setNewDetailText] = useState('')
|
|
|
|
// ویرایش درونخطی ویژگی در زبان فعال
|
|
const [editingDetailIndex, setEditingDetailIndex] = useState<number | null>(null)
|
|
const [editingDetailText, setEditingDetailText] = useState<string>('')
|
|
|
|
const [isSubmitting, setIsSubmitting] = useState(false)
|
|
const [isDeleting, setIsDeleting] = useState(false)
|
|
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false)
|
|
const [errorMsg, setErrorMsg] = useState<string | null>(null)
|
|
|
|
useEffect(() => {
|
|
if (!open) return
|
|
|
|
setActiveLangTab('ar')
|
|
if (item) {
|
|
const cId = item.country?.id || 1
|
|
setCountryId(cId)
|
|
setPrice(item.price ? String(item.price) : '10.00')
|
|
setStatus(item.status ?? 1)
|
|
|
|
// استخراج عناوین ۵ زبانه از دیتابیس
|
|
const parsedTitles = createEmptyTitles()
|
|
if (Array.isArray(item.raw_title)) {
|
|
for (const t of item.raw_title) {
|
|
if (t && typeof t === 'object' && t.language_code && t.title) {
|
|
const lang = t.language_code as SupportedLanguage
|
|
if (lang in parsedTitles) {
|
|
parsedTitles[lang] = t.title
|
|
}
|
|
}
|
|
}
|
|
} else if (item.raw_title && typeof item.raw_title === 'object') {
|
|
for (const lang of ['ar', 'fa', 'en', 'ru', 'id'] as SupportedLanguage[]) {
|
|
if (item.raw_title[lang]) parsedTitles[lang] = String(item.raw_title[lang])
|
|
}
|
|
}
|
|
if (!parsedTitles.ar && !parsedTitles.fa && item.title) {
|
|
parsedTitles.ar = item.title
|
|
parsedTitles.fa = item.title
|
|
}
|
|
setTitles(parsedTitles)
|
|
|
|
// استخراج ویژگیهای ۵ زبانه از دیتابیس (آرایههای کاملاً مستقل جهت جلوگیری از Mutation)
|
|
const parsedDetails = createEmptyDetails()
|
|
if (Array.isArray(item.raw_detail)) {
|
|
for (const d of item.raw_detail) {
|
|
if (d && typeof d === 'object' && d.language_code && d.detail) {
|
|
const lang = d.language_code as SupportedLanguage
|
|
if (lang in parsedDetails) {
|
|
const text = String(d.detail).trim()
|
|
// جلوگیری از موارد تکراری ناشی از ثبتهای قبلی
|
|
if (text && !parsedDetails[lang].includes(text)) {
|
|
parsedDetails[lang].push(text)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (item.raw_detail && typeof item.raw_detail === 'object') {
|
|
for (const lang of ['ar', 'fa', 'en', 'ru', 'id'] as SupportedLanguage[]) {
|
|
if (Array.isArray(item.raw_detail[lang])) {
|
|
const uniqueList: string[] = []
|
|
for (const x of item.raw_detail[lang]) {
|
|
const text = String(x || '').trim()
|
|
if (text && !uniqueList.includes(text)) {
|
|
uniqueList.push(text)
|
|
}
|
|
}
|
|
parsedDetails[lang] = uniqueList
|
|
}
|
|
}
|
|
}
|
|
|
|
if (parsedDetails.ar.length === 0 && parsedDetails.fa.length === 0 && Array.isArray(item.simcard_detail)) {
|
|
const uniqueList: string[] = []
|
|
for (const x of item.simcard_detail) {
|
|
const text = String(x || '').trim()
|
|
if (text && !uniqueList.includes(text)) {
|
|
uniqueList.push(text)
|
|
}
|
|
}
|
|
parsedDetails.ar = [...uniqueList]
|
|
parsedDetails.fa = [...uniqueList]
|
|
}
|
|
setDetails(parsedDetails)
|
|
} else {
|
|
setCountryId(1)
|
|
setPrice('10.00')
|
|
setStatus(1)
|
|
setTitles(createEmptyTitles())
|
|
setDetails(createEmptyDetails())
|
|
}
|
|
|
|
setNewDetailText('')
|
|
setEditingDetailIndex(null)
|
|
setEditingDetailText('')
|
|
setErrorMsg(null)
|
|
setShowDeleteConfirm(false)
|
|
}, [item, open])
|
|
|
|
// کار با ویژگیهای زبان فعال
|
|
const activeDetails = details[activeLangTab] || []
|
|
|
|
const handleTabChange = (langCode: SupportedLanguage) => {
|
|
setActiveLangTab(langCode)
|
|
}
|
|
|
|
const handleTitleChange = (val: string) => {
|
|
setTitles((prev) => ({
|
|
...prev,
|
|
[activeLangTab]: val,
|
|
}))
|
|
}
|
|
|
|
const handleCountryChange = (cId: number) => {
|
|
setCountryId(cId)
|
|
}
|
|
|
|
const handleAddDetail = () => {
|
|
if (!newDetailText.trim()) return
|
|
setDetails((prev) => ({
|
|
...prev,
|
|
[activeLangTab]: [...(prev[activeLangTab] || []), newDetailText.trim()],
|
|
}))
|
|
setNewDetailText('')
|
|
}
|
|
|
|
const handleRemoveDetail = (index: number) => {
|
|
setDetails((prev) => ({
|
|
...prev,
|
|
[activeLangTab]: (prev[activeLangTab] || []).filter((_, i) => i !== index),
|
|
}))
|
|
if (editingDetailIndex === index) {
|
|
setEditingDetailIndex(null)
|
|
setEditingDetailText('')
|
|
}
|
|
}
|
|
|
|
const handleStartEditDetail = (index: number, text: string) => {
|
|
setEditingDetailIndex(index)
|
|
setEditingDetailText(text)
|
|
}
|
|
|
|
const handleSaveEditDetail = (index: number) => {
|
|
if (!editingDetailText.trim()) return
|
|
setDetails((prev) => ({
|
|
...prev,
|
|
[activeLangTab]: (prev[activeLangTab] || []).map((d, i) =>
|
|
i === index ? editingDetailText.trim() : d
|
|
),
|
|
}))
|
|
setEditingDetailIndex(null)
|
|
setEditingDetailText('')
|
|
}
|
|
|
|
const handleCancelEditDetail = () => {
|
|
setEditingDetailIndex(null)
|
|
setEditingDetailText('')
|
|
}
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault()
|
|
|
|
const mainTitle =
|
|
titles.ar.trim() ||
|
|
titles.fa.trim() ||
|
|
titles[activeLangTab].trim() ||
|
|
titles.en.trim() ||
|
|
titles.ru.trim() ||
|
|
titles.id.trim()
|
|
if (!mainTitle) {
|
|
setErrorMsg('لطفاً عنوان بسته سیمکارت را وارد نمایید.')
|
|
return
|
|
}
|
|
|
|
const numPrice = parseFloat(price)
|
|
if (isNaN(numPrice) || numPrice <= 0) {
|
|
setErrorMsg('لطفاً قیمت معتبری وارد نمایید.')
|
|
return
|
|
}
|
|
|
|
// عناوین ۵ زبانه
|
|
const finalTitles: Record<SupportedLanguage, string> = {
|
|
ar: titles.ar.trim() || mainTitle,
|
|
fa: titles.fa.trim() || mainTitle,
|
|
en: titles.en.trim() || mainTitle,
|
|
ru: titles.ru.trim() || mainTitle,
|
|
id: titles.id.trim() || mainTitle,
|
|
}
|
|
|
|
// ویژگیهای ۵ زبانه
|
|
const finalDetails: Record<SupportedLanguage, string[]> = {
|
|
ar: (details.ar || []).filter((d) => d.trim()),
|
|
fa: (details.fa || []).filter((d) => d.trim()),
|
|
en: (details.en || []).filter((d) => d.trim()),
|
|
ru: (details.ru || []).filter((d) => d.trim()),
|
|
id: (details.id || []).filter((d) => d.trim()),
|
|
}
|
|
|
|
// فالبک ویژگیها در صورت خالی بودن
|
|
const fallbackDetails =
|
|
finalDetails.ar.length > 0
|
|
? finalDetails.ar
|
|
: finalDetails.fa.length > 0
|
|
? finalDetails.fa
|
|
: []
|
|
for (const lang of ['ar', 'fa', 'en', 'ru', 'id'] as SupportedLanguage[]) {
|
|
if (finalDetails[lang].length === 0 && fallbackDetails.length > 0) {
|
|
finalDetails[lang] = [...fallbackDetails]
|
|
}
|
|
}
|
|
|
|
setErrorMsg(null)
|
|
setIsSubmitting(true)
|
|
try {
|
|
await onSave(
|
|
{
|
|
title: finalTitles.fa || finalTitles.ar,
|
|
titles: finalTitles,
|
|
country_id: countryId,
|
|
price: numPrice.toFixed(2),
|
|
status,
|
|
simcard_detail: finalDetails.fa.length > 0 ? finalDetails.fa : finalDetails.ar,
|
|
details: finalDetails,
|
|
},
|
|
item?.id
|
|
)
|
|
onOpenChange(false)
|
|
} catch (err: any) {
|
|
setErrorMsg(err?.message || 'خطایی در ثبت اطلاعات رخ داد.')
|
|
} finally {
|
|
setIsSubmitting(false)
|
|
}
|
|
}
|
|
|
|
const handleConfirmDelete = async () => {
|
|
if (!item?.id || !onDelete) return
|
|
setIsDeleting(true)
|
|
try {
|
|
await onDelete(item.id)
|
|
setShowDeleteConfirm(false)
|
|
onOpenChange(false)
|
|
} catch (err: any) {
|
|
setErrorMsg(err?.message || 'خطایی در حذف سیمکارت رخ داد.')
|
|
} finally {
|
|
setIsDeleting(false)
|
|
}
|
|
}
|
|
|
|
const currentLang = LANGUAGES.find((l) => l.code === activeLangTab) || LANGUAGES[0]
|
|
|
|
return (
|
|
<>
|
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
<DialogContent className="sm:max-w-2xl lg:max-w-3xl w-full rounded-2xl bg-card border border-border-soft p-0 max-h-[85vh] flex flex-col overflow-hidden shadow-2xl">
|
|
{/* هدر ثابت در بالای مدال */}
|
|
<DialogHeader className="p-6 pb-4 border-b border-border-soft shrink-0 text-start">
|
|
<DialogTitle className="text-lg font-bold text-foreground">
|
|
{isEditing ? 'ویرایش بسته سیمکارت' : 'افزودن سیمکارت جدید'}
|
|
</DialogTitle>
|
|
<DialogDescription className="text-xs text-grey-3">
|
|
{isEditing
|
|
? 'تغییرات شما در تمام زبانها در دیتابیس ثبت شده و در اپلیکیشن زائرین بر اساس زبان انتخابی کاربر نمایش داده میشود.'
|
|
: 'سیمکارت جدید در دیتابیس ذخیره و در اپلیکیشن فعال خواهد شد.'}
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
|
|
{/* فرم: بدنه اسکرولشونده و فوتر فیکسشده */}
|
|
<form onSubmit={handleSubmit} className="flex flex-col flex-1 overflow-hidden min-h-0">
|
|
{/* بدنه محتوا (تنها بخشی که اسکرول میشود) */}
|
|
<div className="flex-1 overflow-y-auto p-6 space-y-5">
|
|
{errorMsg && (
|
|
<div className="p-3.5 rounded-xl bg-rose-500/10 border border-rose-500/20 text-rose-500 text-xs flex items-center gap-2">
|
|
<Ic name="alert" className="size-4 shrink-0" />
|
|
<span>{errorMsg}</span>
|
|
</div>
|
|
)}
|
|
|
|
{/* ۱. انتخاب کشور مقصد */}
|
|
<div className="space-y-2">
|
|
<label className="text-xs font-bold text-foreground block">
|
|
کشور مقصد <span className="text-rose-500">*</span>
|
|
</label>
|
|
<div className="grid grid-cols-3 gap-3">
|
|
{COUNTRIES.map((c) => {
|
|
const isSelected = countryId === c.id
|
|
return (
|
|
<button
|
|
key={c.id}
|
|
type="button"
|
|
onClick={() => handleCountryChange(c.id)}
|
|
className={`h-12 px-4 rounded-xl border text-xs font-bold transition flex items-center justify-center gap-2.5 ${
|
|
isSelected
|
|
? 'border-primary bg-primary/10 text-primary shadow-xs'
|
|
: 'border-border-soft bg-surface-base/50 text-grey-3 hover:text-foreground hover:bg-card'
|
|
}`}
|
|
>
|
|
<span className="text-lg">{c.flag}</span>
|
|
<span className="text-xs">{c.name}</span>
|
|
</button>
|
|
)
|
|
})}
|
|
</div>
|
|
</div>
|
|
|
|
{/* ۲. تبهای انتخاب ۵ زبان با کامپوننت مشترک ServiceTranslation */}
|
|
<ServiceTranslation
|
|
activeLang={activeLangTab}
|
|
onLangChange={(lang) => handleTabChange(lang)}
|
|
completedLangs={Object.fromEntries(
|
|
Object.entries(titles).map(([k, v]) => [k, Boolean(v?.trim())])
|
|
)}
|
|
primaryLang="ar"
|
|
title="زبان ترجمه و محتوا"
|
|
required
|
|
variant="grid"
|
|
/>
|
|
|
|
{/* ۳. عنوان بسته سیمکارت به زبان فعال */}
|
|
<div className="space-y-2">
|
|
<div className="flex items-center justify-between">
|
|
<label className="text-xs font-bold text-foreground flex items-center gap-1.5">
|
|
<span>عنوان بسته سیمکارت به زبان {currentLang.name}</span>
|
|
<span className="text-[10px] px-1.5 py-0.5 rounded font-mono font-bold bg-primary/10 text-primary border border-primary/20">
|
|
{currentLang.badge}
|
|
</span>
|
|
<span className="text-rose-500">*</span>
|
|
</label>
|
|
<span className="text-[11px] text-grey-3 font-mono">
|
|
{currentLang.dir === 'rtl' ? 'RTL' : 'LTR'}
|
|
</span>
|
|
</div>
|
|
<Input
|
|
value={titles[activeLangTab] || ''}
|
|
onChange={(e) => handleTitleChange(e.target.value)}
|
|
dir={currentLang.dir}
|
|
placeholder={`عنوان بسته به زبان ${currentLang.name}...`}
|
|
className="h-11 text-xs bg-surface-base rounded-xl border-border-soft"
|
|
required={activeLangTab === 'fa'}
|
|
/>
|
|
</div>
|
|
|
|
{/* ۴. قیمت و وضعیت در اپلیکیشن */}
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
<div className="space-y-2">
|
|
<label className="text-xs font-bold text-foreground block">
|
|
قیمت به دلار ($) <span className="text-rose-500">*</span>
|
|
</label>
|
|
<div className="relative">
|
|
<Input
|
|
type="number"
|
|
step="0.01"
|
|
min="0"
|
|
value={price}
|
|
onChange={(e) => setPrice(e.target.value)}
|
|
placeholder="10.00"
|
|
className="h-11 text-xs bg-surface-base rounded-xl border-border-soft font-mono ps-8"
|
|
required
|
|
/>
|
|
<span className="absolute start-3 top-1/2 -translate-y-1/2 text-grey-3 font-mono text-xs">
|
|
$
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<label className="text-xs font-bold text-foreground block">وضعیت در اپلیکیشن</label>
|
|
<select
|
|
value={status}
|
|
onChange={(e) => setStatus(parseInt(e.target.value, 10))}
|
|
className="w-full h-11 text-xs bg-surface-base rounded-xl border border-border-soft px-3 text-foreground font-medium focus:outline-none focus:ring-1 focus:ring-primary"
|
|
>
|
|
<option value={1}>فعال در اپلیکیشن</option>
|
|
<option value={2}>ناموجود (Out of Stock)</option>
|
|
<option value={3}>غیرفعال</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
{/* ۵. ویژگیها و جزئیات بسته به زبان فعال */}
|
|
<div className="space-y-2">
|
|
<div className="flex items-center justify-between">
|
|
<label className="text-xs font-bold text-foreground flex items-center gap-1.5">
|
|
<span>ویژگیها و جزئیات بسته به زبان {currentLang.name}</span>
|
|
<span className="text-[10px] px-1.5 py-0.5 rounded font-mono font-bold bg-primary/10 text-primary border border-primary/20">
|
|
{currentLang.badge}
|
|
</span>
|
|
</label>
|
|
<span className="text-[11px] text-grey-3">
|
|
{activeDetails.length} ویژگی ثبتشده برای {currentLang.name}
|
|
</span>
|
|
</div>
|
|
|
|
{/* ورودی افزودن ویژگی جدید */}
|
|
<div className="flex items-center gap-2">
|
|
<Input
|
|
value={newDetailText}
|
|
onChange={(e) => setNewDetailText(e.target.value)}
|
|
onKeyDown={(e) => {
|
|
if (e.key === 'Enter') {
|
|
e.preventDefault()
|
|
handleAddDetail()
|
|
}
|
|
}}
|
|
dir={currentLang.dir}
|
|
placeholder={`افزودن ویژگی به زبان ${currentLang.name} (مثلاً حجم اینترنت، دقایق مکالمه، فعالسازی)...`}
|
|
className="h-10 text-xs bg-surface-base rounded-xl border-border-soft flex-1"
|
|
/>
|
|
<Button
|
|
type="button"
|
|
onClick={handleAddDetail}
|
|
disabled={!newDetailText.trim()}
|
|
className="h-10 px-4 rounded-xl text-xs font-bold bg-primary text-primary-foreground hover:bg-primary/90 gap-1.5 shrink-0"
|
|
>
|
|
<Ic name="plus" className="size-3.5" />
|
|
<span>افزودن</span>
|
|
</Button>
|
|
</div>
|
|
|
|
{/* لیست ویژگیهای ثبتشده در زبان فعال */}
|
|
{activeDetails.length === 0 ? (
|
|
<div className="p-4 rounded-xl border border-dashed border-border-soft text-center text-grey-3 text-xs">
|
|
هنوز ویژگی خاصی به زبان {currentLang.name} اضافه نشده است. میتوانید با فیلد بالا ویژگی جدید ثبت کنید.
|
|
</div>
|
|
) : (
|
|
<div className="space-y-2">
|
|
{activeDetails.map((itemText, idx) => (
|
|
<div
|
|
key={idx}
|
|
className="p-3 rounded-xl border border-border-soft bg-surface-base/60 text-xs transition"
|
|
>
|
|
{editingDetailIndex === idx ? (
|
|
<div className="flex items-center gap-2">
|
|
<Input
|
|
value={editingDetailText}
|
|
onChange={(e) => setEditingDetailText(e.target.value)}
|
|
onKeyDown={(e) => {
|
|
if (e.key === 'Enter') {
|
|
e.preventDefault()
|
|
handleSaveEditDetail(idx)
|
|
}
|
|
}}
|
|
dir={currentLang.dir}
|
|
className="h-9 text-xs bg-card rounded-lg flex-1"
|
|
autoFocus
|
|
/>
|
|
<Button
|
|
type="button"
|
|
size="sm"
|
|
onClick={() => handleSaveEditDetail(idx)}
|
|
className="h-9 px-3 text-xs bg-primary text-white rounded-lg gap-1 shrink-0"
|
|
>
|
|
<Ic name="check" className="size-3.5" />
|
|
<span>ذخیره</span>
|
|
</Button>
|
|
<Button
|
|
type="button"
|
|
size="sm"
|
|
variant="ghost"
|
|
onClick={handleCancelEditDetail}
|
|
className="h-9 px-2 text-xs text-grey-3 hover:text-foreground shrink-0"
|
|
title="انصراف"
|
|
>
|
|
<Ic name="close" className="size-3.5" />
|
|
</Button>
|
|
</div>
|
|
) : (
|
|
<div className="flex items-center justify-between gap-3">
|
|
<div className="flex items-center gap-2 truncate" dir={currentLang.dir}>
|
|
<span className="size-1.5 rounded-full bg-primary shrink-0" />
|
|
<span className="text-foreground font-medium leading-relaxed truncate">
|
|
{itemText}
|
|
</span>
|
|
</div>
|
|
<div className="flex items-center gap-1 shrink-0">
|
|
<button
|
|
type="button"
|
|
onClick={() => handleStartEditDetail(idx, itemText)}
|
|
className="text-grey-3 hover:text-primary p-1.5 rounded-lg hover:bg-primary/10 transition"
|
|
title="ویرایش ویژگی"
|
|
>
|
|
<Ic name="edit" className="size-3.5" />
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={() => handleRemoveDetail(idx)}
|
|
className="text-grey-3 hover:text-rose-500 p-1.5 rounded-lg hover:bg-rose-500/10 transition"
|
|
title="حذف ویژگی"
|
|
>
|
|
<Ic name="trash" className="size-3.5" />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* فوتر فیکسشده در پایین */}
|
|
<DialogFooter className="p-4 sm:px-6 bg-card border-t border-border-soft shrink-0 flex flex-row items-center justify-between w-full gap-3">
|
|
{/* دکمه حذف در سمت راست (Start در RTL) */}
|
|
<div>
|
|
{isEditing && onDelete && (
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
disabled={isSubmitting || isDeleting}
|
|
onClick={() => setShowDeleteConfirm(true)}
|
|
className="rounded-xl text-xs h-10 px-3.5 text-rose-500 hover:text-rose-600 hover:bg-rose-500/10 gap-1.5"
|
|
title="حذف بسته سیمکارت"
|
|
>
|
|
<Ic name="trash" className="size-4" />
|
|
<span>حذف بسته</span>
|
|
</Button>
|
|
)}
|
|
</div>
|
|
|
|
{/* دکمههای انصراف و ذخیره در سمت چپ (End در RTL) */}
|
|
<div className="flex items-center gap-2">
|
|
<DialogClose asChild>
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
disabled={isSubmitting || isDeleting}
|
|
className="rounded-xl text-xs h-10 px-4 text-grey-3 hover:text-foreground"
|
|
>
|
|
انصراف
|
|
</Button>
|
|
</DialogClose>
|
|
<Button
|
|
type="submit"
|
|
disabled={isSubmitting || isDeleting}
|
|
className="rounded-xl text-xs font-bold h-10 px-6 bg-primary text-primary-foreground hover:bg-primary/90 shadow-xs"
|
|
>
|
|
{isSubmitting ? 'در حال ذخیره...' : 'ذخیره'}
|
|
</Button>
|
|
</div>
|
|
</DialogFooter>
|
|
</form>
|
|
</DialogContent>
|
|
</Dialog>
|
|
|
|
{/* دیالوگ اعتبارسنجی و تایید حذف برای جلوگیری از حذف تصادفی */}
|
|
{showDeleteConfirm && (
|
|
<Dialog open={showDeleteConfirm} onOpenChange={setShowDeleteConfirm}>
|
|
<DialogContent className="sm:max-w-md rounded-2xl bg-card border border-border-soft p-6 shadow-2xl">
|
|
<DialogHeader className="space-y-2 text-start">
|
|
<div className="size-11 rounded-2xl bg-rose-500/10 text-rose-500 flex items-center justify-center mb-1">
|
|
<Ic name="trash" className="size-5" />
|
|
</div>
|
|
<DialogTitle className="text-base font-bold text-foreground">
|
|
حذف بسته سیمکارت
|
|
</DialogTitle>
|
|
<DialogDescription className="text-xs text-grey-3 leading-relaxed">
|
|
آیا از حذف بسته سیمکارت «{item?.title}» از پایگاهداده اطمینان دارید؟ با حذف این مورد، دیگر در اپلیکیشن زائرین نمایش داده نخواهد شد و این عملیات غیرقابل بازگشت است.
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
|
|
<DialogFooter className="mt-5 flex flex-row items-center gap-2 sm:justify-start">
|
|
<Button
|
|
type="button"
|
|
disabled={isDeleting}
|
|
onClick={handleConfirmDelete}
|
|
className="rounded-xl text-xs font-bold h-9 px-4 bg-rose-500 hover:bg-rose-600 text-white flex-1"
|
|
>
|
|
{isDeleting ? 'در حال حذف...' : 'تایید و حذف'}
|
|
</Button>
|
|
<DialogClose asChild>
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
disabled={isDeleting}
|
|
className="rounded-xl text-xs h-9 px-4 flex-1"
|
|
>
|
|
انصراف
|
|
</Button>
|
|
</DialogClose>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
)}
|
|
</>
|
|
)
|
|
}
|