15 changed files with 1382 additions and 2445 deletions
-
7src/features/custom-trip/CustomTripDestinationDetailPage.tsx
-
27src/features/custom-trip/components/CustomTripDestinationsTab.tsx
-
65src/features/custom-trip/components/CustomTripOrderReviewDialog.tsx
-
14src/features/custom-trip/components/CustomTripOrdersTab.tsx
-
2777src/features/custom-trip/services/custom-trip-api.ts
-
11src/features/custom-trip/types.ts
-
32src/features/settings/SettingsPage.tsx
-
714src/features/settings/components/SocialMediaSettingsTab.tsx
-
25src/features/settings/services/settings-api.ts
-
34src/features/settings/types.ts
-
7src/icons.tsx
-
35src/locales/ar.json
-
35src/locales/en.json
-
35src/locales/fa.json
-
9src/services/http.ts
2777
src/features/custom-trip/services/custom-trip-api.ts
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,714 @@ |
|||
import * as React from 'react' |
|||
import { useTranslation } from '@/i18n' |
|||
import { Card, CardContent } from '@/components/ui/card' |
|||
import { Badge } from '@/components/ui/badge' |
|||
import { Button } from '@/components/ui/button' |
|||
import { Switch } from '@/components/ui/switch' |
|||
import { |
|||
Dialog, |
|||
DialogContent, |
|||
DialogHeader, |
|||
DialogTitle, |
|||
DialogFooter, |
|||
} from '@/components/ui/dialog' |
|||
import { Ic, type IconName } from '@/icons' |
|||
import { toast } from '@/stores/toast' |
|||
import { |
|||
fetchSocialMediaSettings, |
|||
saveSocialMediaSettings, |
|||
} from '../services/settings-api' |
|||
import type { |
|||
SocialMediaItem, |
|||
SocialMediaContactUs, |
|||
} from '../types' |
|||
|
|||
interface PlatformMeta { |
|||
key: string |
|||
name: string |
|||
nameEn: string |
|||
icon: IconName |
|||
brandColor: string |
|||
defaultUrl: string |
|||
placeholder: string |
|||
} |
|||
|
|||
const PRESET_PLATFORMS: Record<string, PlatformMeta> = { |
|||
instagram: { |
|||
key: 'instagram', |
|||
name: 'اینستاگرام', |
|||
nameEn: 'Instagram', |
|||
icon: 'instagram', |
|||
brandColor: '#E1306C', |
|||
defaultUrl: 'https://instagram.com/', |
|||
placeholder: 'https://instagram.com/aqila_group', |
|||
}, |
|||
telegram: { |
|||
key: 'telegram', |
|||
name: 'تلگرام', |
|||
nameEn: 'Telegram', |
|||
icon: 'telegram', |
|||
brandColor: '#229ED9', |
|||
defaultUrl: 'https://t.me/', |
|||
placeholder: 'https://t.me/aqila_official', |
|||
}, |
|||
whatsapp: { |
|||
key: 'whatsapp', |
|||
name: 'واتساپ', |
|||
nameEn: 'WhatsApp', |
|||
icon: 'whatsapp', |
|||
brandColor: '#25D366', |
|||
defaultUrl: 'https://wa.me/', |
|||
placeholder: 'https://wa.me/989123456789', |
|||
}, |
|||
youtube: { |
|||
key: 'youtube', |
|||
name: 'یوتیوب', |
|||
nameEn: 'YouTube', |
|||
icon: 'youtube', |
|||
brandColor: '#FF0000', |
|||
defaultUrl: 'https://youtube.com/@', |
|||
placeholder: 'https://youtube.com/@aqila_tours', |
|||
}, |
|||
x: { |
|||
key: 'x', |
|||
name: 'ایکس / توییتر', |
|||
nameEn: 'X (Twitter)', |
|||
icon: 'twitter', |
|||
brandColor: '#E2E8F0', |
|||
defaultUrl: 'https://x.com/', |
|||
placeholder: 'https://x.com/aqila', |
|||
}, |
|||
linkedin: { |
|||
key: 'linkedin', |
|||
name: 'لینکدین', |
|||
nameEn: 'LinkedIn', |
|||
icon: 'linkedin', |
|||
brandColor: '#0A66C2', |
|||
defaultUrl: 'https://linkedin.com/company/', |
|||
placeholder: 'https://linkedin.com/company/aqila', |
|||
}, |
|||
facebook: { |
|||
key: 'facebook', |
|||
name: 'فیسبوک', |
|||
nameEn: 'Facebook', |
|||
icon: 'facebook', |
|||
brandColor: '#1877F2', |
|||
defaultUrl: 'https://facebook.com/', |
|||
placeholder: 'https://facebook.com/aqila', |
|||
}, |
|||
tiktok: { |
|||
key: 'tiktok', |
|||
name: 'تیکتاک', |
|||
nameEn: 'TikTok', |
|||
icon: 'tiktok', |
|||
brandColor: '#F43F5E', |
|||
defaultUrl: 'https://tiktok.com/@', |
|||
placeholder: 'https://tiktok.com/@aqila', |
|||
}, |
|||
aparat: { |
|||
key: 'aparat', |
|||
name: 'آپارات', |
|||
nameEn: 'Aparat', |
|||
icon: 'video', |
|||
brandColor: '#EA1D5D', |
|||
defaultUrl: 'https://aparat.com/', |
|||
placeholder: 'https://aparat.com/aqila', |
|||
}, |
|||
bale: { |
|||
key: 'bale', |
|||
name: 'پیامرسان بله', |
|||
nameEn: 'Bale', |
|||
icon: 'message', |
|||
brandColor: '#10B981', |
|||
defaultUrl: 'https://ble.ir/', |
|||
placeholder: 'https://ble.ir/aqila_group', |
|||
}, |
|||
eitaa: { |
|||
key: 'eitaa', |
|||
name: 'پیامرسان ایتا', |
|||
nameEn: 'Eitaa', |
|||
icon: 'message', |
|||
brandColor: '#F97316', |
|||
defaultUrl: 'https://eitaa.com/', |
|||
placeholder: 'https://eitaa.com/aqila_official', |
|||
}, |
|||
threads: { |
|||
key: 'threads', |
|||
name: 'تردز', |
|||
nameEn: 'Threads', |
|||
icon: 'share2', |
|||
brandColor: '#F1F5F9', |
|||
defaultUrl: 'https://threads.net/@', |
|||
placeholder: 'https://threads.net/@aqila', |
|||
}, |
|||
website: { |
|||
key: 'website', |
|||
name: 'وبسایت رسمی', |
|||
nameEn: 'Official Website', |
|||
icon: 'globe', |
|||
brandColor: '#38BDF8', |
|||
defaultUrl: 'https://', |
|||
placeholder: 'https://aqila.ir', |
|||
}, |
|||
} |
|||
|
|||
export function SocialMediaSettingsTab() { |
|||
const { t } = useTranslation() |
|||
|
|||
const [loading, setLoading] = React.useState(true) |
|||
const [saving, setSaving] = React.useState(false) |
|||
const [items, setItems] = React.useState<SocialMediaItem[]>([]) |
|||
const [contactus, setContactus] = React.useState<SocialMediaContactUs>({ |
|||
phone_number: '', |
|||
whatsapp_number: '', |
|||
address: '', |
|||
}) |
|||
|
|||
// Modal State for adding new platform
|
|||
const [addModalOpen, setAddModalOpen] = React.useState(false) |
|||
const [selectedPlatformKey, setSelectedPlatformKey] = React.useState('telegram') |
|||
const [newChannelName, setNewChannelName] = React.useState('') |
|||
const [newChannelLink, setNewChannelLink] = React.useState('') |
|||
|
|||
const loadData = React.useCallback(async () => { |
|||
setLoading(true) |
|||
try { |
|||
const res = await fetchSocialMediaSettings() |
|||
setItems(res.items || []) |
|||
setContactus(res.contactus || {}) |
|||
} catch (err: any) { |
|||
console.error('Error fetching social media settings:', err) |
|||
toast.error(err?.message || t('settings.socialFetchError')) |
|||
} finally { |
|||
setLoading(false) |
|||
} |
|||
}, [t]) |
|||
|
|||
React.useEffect(() => { |
|||
loadData() |
|||
}, [loadData]) |
|||
|
|||
const handleOpenAddModal = (platformKey?: string) => { |
|||
const pKey = platformKey || 'telegram' |
|||
const preset = PRESET_PLATFORMS[pKey] || PRESET_PLATFORMS.telegram |
|||
setSelectedPlatformKey(pKey) |
|||
setNewChannelName(preset.name) |
|||
setNewChannelLink(preset.defaultUrl) |
|||
setAddModalOpen(true) |
|||
} |
|||
|
|||
const handleSelectPreset = (key: string) => { |
|||
const preset = PRESET_PLATFORMS[key] |
|||
setSelectedPlatformKey(key) |
|||
if (preset) { |
|||
setNewChannelName(preset.name) |
|||
setNewChannelLink(preset.defaultUrl) |
|||
} |
|||
} |
|||
|
|||
const handleAddChannel = () => { |
|||
if (!newChannelLink.trim()) { |
|||
toast.error('لطفاً آدرس پیوند (URL) را وارد نمایید.') |
|||
return |
|||
} |
|||
|
|||
const newItem: SocialMediaItem = { |
|||
id: `social-${selectedPlatformKey}-${Date.now()}`, |
|||
platform: selectedPlatformKey, |
|||
name: newChannelName.trim() || selectedPlatformKey, |
|||
link: newChannelLink.trim(), |
|||
is_active: true, |
|||
} |
|||
|
|||
setItems((prev) => [...prev, newItem]) |
|||
setAddModalOpen(false) |
|||
toast.success(t('settings.channelAdded')) |
|||
} |
|||
|
|||
const handleItemChange = (index: number, field: keyof SocialMediaItem, value: any) => { |
|||
setItems((prev) => { |
|||
const next = [...prev] |
|||
next[index] = { ...next[index], [field]: value } |
|||
return next |
|||
}) |
|||
} |
|||
|
|||
const handleDeleteItem = (index: number) => { |
|||
setItems((prev) => prev.filter((_, idx) => idx !== index)) |
|||
} |
|||
|
|||
const handleCopyLink = (link: string) => { |
|||
if (!link) return |
|||
navigator.clipboard.writeText(link) |
|||
toast.success(t('settings.linkCopied')) |
|||
} |
|||
|
|||
const handleSaveToDatabase = async () => { |
|||
setSaving(true) |
|||
try { |
|||
const res = await saveSocialMediaSettings({ |
|||
items, |
|||
contactus, |
|||
}) |
|||
if (res.items) { |
|||
setItems(res.items) |
|||
} |
|||
if (res.contactus) { |
|||
setContactus(res.contactus) |
|||
} |
|||
toast.success(res.message || t('settings.socialSaveSuccess')) |
|||
} catch (err: any) { |
|||
console.error('Error saving social media settings:', err) |
|||
toast.error(err?.message || t('settings.socialSaveError')) |
|||
} finally { |
|||
setSaving(false) |
|||
} |
|||
} |
|||
|
|||
const activeCount = items.filter((i) => i.is_active).length |
|||
|
|||
return ( |
|||
<div className="space-y-6"> |
|||
{/* ۱. کارت سربرگ و راهنمای تنظیمات شبکههای اجتماعی (کالیبره حالت دارک) */} |
|||
<Card className="border border-white/[0.08] bg-[#1e2129] shadow-md"> |
|||
<CardContent className="p-6"> |
|||
<div className="flex flex-col gap-5 lg:flex-row lg:items-center lg:justify-between"> |
|||
<div className="flex items-start gap-4"> |
|||
<div className="flex size-12 shrink-0 items-center justify-center rounded-2xl bg-primary/15 text-primary border border-primary/25 shadow-inner"> |
|||
<Ic name="share" className="size-6" /> |
|||
</div> |
|||
<div className="space-y-1.5"> |
|||
<div className="flex items-center gap-2.5 flex-wrap"> |
|||
<h2 className="text-base sm:text-lg font-black text-white"> |
|||
{t('settings.socialTabTitle')} |
|||
</h2> |
|||
<Badge variant="outline" className="border-emerald-500/30 bg-emerald-500/10 text-emerald-400 font-bold text-xs"> |
|||
{activeCount} {t('settings.activeStatus')} |
|||
</Badge> |
|||
<Badge variant="outline" className="border-white/10 bg-white/[0.04] text-slate-300 font-mono text-[11px]"> |
|||
PostgreSQL Live Sync |
|||
</Badge> |
|||
</div> |
|||
<p className="text-xs text-slate-400 leading-relaxed max-w-2xl"> |
|||
{t('settings.socialTabSubtitle')} |
|||
</p> |
|||
</div> |
|||
</div> |
|||
|
|||
<div className="flex items-center gap-2.5 shrink-0 flex-wrap sm:flex-nowrap"> |
|||
<Button |
|||
type="button" |
|||
variant="outline" |
|||
size="sm" |
|||
onClick={() => handleOpenAddModal()} |
|||
className="gap-2 rounded-xl text-xs font-bold border-white/15 bg-white/[0.04] hover:bg-white/[0.08] text-slate-100 hover:text-white" |
|||
> |
|||
<Ic name="plus" className="size-4 text-primary" /> |
|||
<span>{t('settings.addSocialBtn')}</span> |
|||
</Button> |
|||
|
|||
<Button |
|||
type="button" |
|||
size="sm" |
|||
disabled={saving || loading} |
|||
onClick={handleSaveToDatabase} |
|||
className="gap-2 rounded-xl text-xs font-black bg-gradient-to-r from-[#d09460] to-[#b37745] hover:from-[#dd9e69] hover:to-[#be804c] text-white shadow-md shadow-[#d09460]/20 transition-all active:scale-[0.98]" |
|||
> |
|||
<Ic name="save" className={`size-4 ${saving ? 'animate-spin' : ''}`} /> |
|||
<span>{saving ? t('settings.saving') : t('settings.saveSocialBtn')}</span> |
|||
</Button> |
|||
</div> |
|||
</div> |
|||
|
|||
{/* نوار انتخاب سریع پلتفرمهای مطرح (کالیبره و منسجم در تم تیره) */} |
|||
<div className="mt-5 pt-4 border-t border-white/[0.07]"> |
|||
<div className="text-[11px] font-bold text-slate-400 mb-2.5 flex items-center gap-1.5"> |
|||
<Ic name="sparkles" className="size-3.5 text-primary" /> |
|||
<span>پلتفرمهای پیشنهادی جهت افزودن فوری:</span> |
|||
</div> |
|||
<div className="flex items-center gap-2 overflow-x-auto pb-1.5 scrollbar-none"> |
|||
{Object.values(PRESET_PLATFORMS).map((preset) => { |
|||
const isAlreadyAdded = items.some((it) => it.platform.toLowerCase() === preset.key.toLowerCase()) |
|||
return ( |
|||
<button |
|||
key={preset.key} |
|||
type="button" |
|||
onClick={() => handleOpenAddModal(preset.key)} |
|||
className={`inline-flex items-center gap-2 px-3 py-1.5 rounded-xl border text-xs font-medium shrink-0 transition-all ${ |
|||
isAlreadyAdded |
|||
? 'border-emerald-500/35 bg-emerald-500/10 text-emerald-300' |
|||
: 'border-white/[0.08] bg-[#16181f] hover:bg-[#252833] hover:border-white/[0.2] text-slate-200' |
|||
}`}
|
|||
> |
|||
<span |
|||
className="size-2 rounded-full shrink-0" |
|||
style={{ backgroundColor: preset.brandColor }} |
|||
/> |
|||
<Ic |
|||
name={preset.icon} |
|||
className="size-3.5 shrink-0" |
|||
style={{ color: preset.brandColor }} |
|||
/> |
|||
<span>{preset.name}</span> |
|||
{isAlreadyAdded ? ( |
|||
<span className="text-[10px] size-4 rounded-full bg-emerald-500/20 text-emerald-400 flex items-center justify-center font-bold"> |
|||
✓ |
|||
</span> |
|||
) : ( |
|||
<span className="text-[10px] text-slate-500 group-hover:text-white">+</span> |
|||
)} |
|||
</button> |
|||
) |
|||
})} |
|||
</div> |
|||
</div> |
|||
</CardContent> |
|||
</Card> |
|||
|
|||
{/* ۲. فهرست شبکههای اجتماعی ثبتشده */} |
|||
{loading ? ( |
|||
<Card className="border border-white/[0.08] bg-[#1e2129] p-12 text-center shadow-xs"> |
|||
<div className="flex flex-col items-center justify-center gap-3"> |
|||
<span className="size-8 rounded-full border-3 border-primary/20 border-t-primary animate-spin" /> |
|||
<p className="text-xs text-slate-300 font-bold">در حال دریافت شبکههای اجتماعی از دیتابیس...</p> |
|||
</div> |
|||
</Card> |
|||
) : items.length === 0 ? ( |
|||
<Card className="border-dashed border-2 border-white/[0.1] bg-[#1e2129]/60 p-12 text-center shadow-xs"> |
|||
<div className="max-w-md mx-auto space-y-4"> |
|||
<div className="size-16 rounded-3xl bg-primary/10 text-primary border border-primary/20 flex items-center justify-center mx-auto shadow-inner"> |
|||
<Ic name="share" className="size-8" /> |
|||
</div> |
|||
<div className="space-y-1"> |
|||
<h3 className="text-base font-black text-white"> |
|||
{t('settings.noSocialChannels')} |
|||
</h3> |
|||
<p className="text-xs text-slate-400"> |
|||
{t('settings.noSocialChannelsDesc')} |
|||
</p> |
|||
</div> |
|||
<Button |
|||
type="button" |
|||
onClick={() => handleOpenAddModal('instagram')} |
|||
className="gap-2 rounded-xl text-xs font-bold bg-primary text-white hover:bg-primary/90" |
|||
> |
|||
<Ic name="plus" className="size-4" /> |
|||
<span>{t('settings.addSocialBtn')}</span> |
|||
</Button> |
|||
</div> |
|||
</Card> |
|||
) : ( |
|||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4"> |
|||
{items.map((item, index) => { |
|||
const preset = PRESET_PLATFORMS[item.platform.toLowerCase()] || { |
|||
key: item.platform, |
|||
name: item.name || item.platform, |
|||
nameEn: item.platform, |
|||
icon: 'link' as IconName, |
|||
brandColor: '#38BDF8', |
|||
defaultUrl: '', |
|||
placeholder: 'https://...', |
|||
} |
|||
|
|||
return ( |
|||
<Card |
|||
key={item.id || index} |
|||
className={`border transition-all duration-200 ${ |
|||
item.is_active |
|||
? 'border-white/[0.1] bg-[#1e2129] shadow-sm hover:border-white/[0.18]' |
|||
: 'border-white/[0.05] bg-[#171920] opacity-65' |
|||
}`}
|
|||
> |
|||
<CardContent className="p-4 sm:p-5 space-y-4"> |
|||
{/* ردیف بالا: آیکون برند، عنوان پلتفرم، بج وضعیت، سوییچ و دکمه حذف */} |
|||
<div className="flex items-center justify-between gap-3"> |
|||
<div className="flex items-center gap-3"> |
|||
<div |
|||
className="size-11 rounded-2xl flex items-center justify-center shrink-0 border border-white/[0.08] bg-[#15171e] shadow-inner" |
|||
style={{ color: preset.brandColor }} |
|||
> |
|||
<Ic name={preset.icon} className="size-5.5" /> |
|||
</div> |
|||
<div className="space-y-1"> |
|||
<div className="flex items-center gap-2"> |
|||
<span className="text-sm font-black text-white"> |
|||
{preset.name} |
|||
</span> |
|||
<span className="text-[10px] text-slate-400 uppercase font-mono px-1.5 py-0.5 rounded bg-white/[0.05] border border-white/[0.05]"> |
|||
{preset.nameEn || item.platform} |
|||
</span> |
|||
</div> |
|||
<div> |
|||
{item.is_active ? ( |
|||
<span className="inline-flex items-center gap-1.5 px-2 py-0.5 rounded-md text-[10.5px] font-bold bg-emerald-500/15 text-emerald-400 border border-emerald-500/25"> |
|||
<span className="size-1.5 rounded-full bg-emerald-400 animate-pulse" /> |
|||
<span>{t('settings.activeStatus')}</span> |
|||
</span> |
|||
) : ( |
|||
<span className="inline-flex items-center gap-1.5 px-2 py-0.5 rounded-md text-[10.5px] font-medium bg-slate-800 text-slate-400 border border-white/[0.06]"> |
|||
<span>{t('settings.inactiveStatus')}</span> |
|||
</span> |
|||
)} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div className="flex items-center gap-2.5 shrink-0"> |
|||
<Switch |
|||
checked={item.is_active} |
|||
onCheckedChange={(checked) => handleItemChange(index, 'is_active', checked)} |
|||
/> |
|||
<button |
|||
type="button" |
|||
onClick={() => handleDeleteItem(index)} |
|||
title={t('settings.deleteChannel')} |
|||
className="size-8.5 rounded-xl text-slate-400 hover:text-rose-400 hover:bg-rose-500/10 flex items-center justify-center transition-colors" |
|||
> |
|||
<Ic name="trash" className="size-4" /> |
|||
</button> |
|||
</div> |
|||
</div> |
|||
|
|||
{/* فیلدهای ورودی عنوان نمایشی و آدرس اینترنتی (با اصلاح کامل مشکل همپوشانی) */} |
|||
<div className="space-y-3 pt-1"> |
|||
<div className="space-y-1"> |
|||
<label className="text-xs font-semibold text-slate-300"> |
|||
{t('settings.channelName')} |
|||
</label> |
|||
<input |
|||
type="text" |
|||
value={item.name} |
|||
onChange={(e) => handleItemChange(index, 'name', e.target.value)} |
|||
placeholder="مثال: کانال رسمی اطلاعرسانی عقیله" |
|||
className="w-full h-9.5 rounded-xl bg-[#14161c] border border-white/[0.1] px-3.5 text-xs font-medium text-slate-100 placeholder:text-slate-500 outline-none transition-colors focus:border-primary focus:ring-1 focus:ring-primary/40" |
|||
/> |
|||
</div> |
|||
|
|||
<div className="space-y-1"> |
|||
<label className="text-xs font-semibold text-slate-300"> |
|||
{t('settings.channelLink')} |
|||
</label> |
|||
{/* کانتینر LTR اختصاصی جهت جلوگیری از هرگونه تداخل آیکونها با متن */} |
|||
<div className="relative flex items-center" dir="ltr"> |
|||
<input |
|||
type="text" |
|||
dir="ltr" |
|||
value={item.link} |
|||
onChange={(e) => handleItemChange(index, 'link', e.target.value)} |
|||
placeholder={preset.placeholder} |
|||
className="w-full h-9.5 rounded-xl bg-[#14161c] border border-white/[0.1] pl-3.5 pr-20 text-xs font-mono text-slate-100 placeholder:text-slate-500 outline-none transition-colors focus:border-primary focus:ring-1 focus:ring-primary/40 text-left" |
|||
/> |
|||
{/* دکمههای عملیات ثابت در سمت راست فیلد LTR */} |
|||
<div className="absolute right-2 top-1/2 -translate-y-1/2 flex items-center gap-1"> |
|||
<button |
|||
type="button" |
|||
onClick={() => handleCopyLink(item.link)} |
|||
title={t('settings.copyLinkTooltip')} |
|||
className="size-7 rounded-lg bg-white/[0.04] hover:bg-white/[0.1] border border-white/[0.06] text-slate-300 hover:text-white flex items-center justify-center transition-colors" |
|||
> |
|||
<Ic name="copy" className="size-3.5" /> |
|||
</button> |
|||
{item.link ? ( |
|||
<a |
|||
href={item.link} |
|||
target="_blank" |
|||
rel="noopener noreferrer" |
|||
title={t('settings.testLinkTooltip')} |
|||
className="size-7 rounded-lg bg-primary/10 hover:bg-primary/20 border border-primary/20 text-primary flex items-center justify-center transition-colors" |
|||
> |
|||
<Ic name="external" className="size-3.5" /> |
|||
</a> |
|||
) : null} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</CardContent> |
|||
</Card> |
|||
) |
|||
})} |
|||
</div> |
|||
)} |
|||
|
|||
{/* ۳. کارت اطلاعات تماس تکمیلی (Contact Us - کالیبره در تم تیره) */} |
|||
<Card className="border border-white/[0.08] bg-[#1e2129] shadow-md"> |
|||
<CardContent className="p-6 space-y-5"> |
|||
<div className="flex items-center gap-3.5"> |
|||
<div className="flex size-11 items-center justify-center rounded-2xl bg-amber-500/15 text-amber-400 border border-amber-500/25 shadow-inner"> |
|||
<Ic name="phone" className="size-5.5" /> |
|||
</div> |
|||
<div> |
|||
<h3 className="text-sm sm:text-base font-black text-white"> |
|||
{t('settings.contactInfoCardTitle')} |
|||
</h3> |
|||
<p className="text-xs text-slate-400 mt-0.5"> |
|||
{t('settings.contactInfoCardDesc')} |
|||
</p> |
|||
</div> |
|||
</div> |
|||
|
|||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 pt-1"> |
|||
{/* شمارههای پشتیبانی */} |
|||
<div className="space-y-1.5"> |
|||
<label className="text-xs font-semibold text-slate-300 flex items-center gap-1.5"> |
|||
<Ic name="phone" className="size-3.5 text-primary" /> |
|||
<span>{t('settings.phoneNumberLabel')}</span> |
|||
</label> |
|||
<div className="relative flex items-center" dir="ltr"> |
|||
<input |
|||
type="text" |
|||
dir="ltr" |
|||
value={contactus.phone_number || ''} |
|||
onChange={(e) => setContactus((prev) => ({ ...prev, phone_number: e.target.value }))} |
|||
placeholder="+982188888888 | +9647700000000" |
|||
className="w-full h-10 rounded-xl bg-[#14161c] border border-white/[0.1] px-3.5 text-xs font-mono text-slate-100 placeholder:text-slate-500 outline-none transition-colors focus:border-primary focus:ring-1 focus:ring-primary/40 text-left" |
|||
/> |
|||
</div> |
|||
</div> |
|||
|
|||
{/* شماره اختصاصی واتساپ */} |
|||
<div className="space-y-1.5"> |
|||
<label className="text-xs font-semibold text-slate-300 flex items-center gap-1.5"> |
|||
<Ic name="whatsapp" className="size-3.5 text-emerald-400" /> |
|||
<span>{t('settings.whatsappNumberLabel')}</span> |
|||
</label> |
|||
<div className="relative flex items-center" dir="ltr"> |
|||
<input |
|||
type="text" |
|||
dir="ltr" |
|||
value={contactus.whatsapp_number || ''} |
|||
onChange={(e) => setContactus((prev) => ({ ...prev, whatsapp_number: e.target.value }))} |
|||
placeholder="+989120000000" |
|||
className="w-full h-10 rounded-xl bg-[#14161c] border border-white/[0.1] px-3.5 text-xs font-mono text-slate-100 placeholder:text-slate-500 outline-none transition-colors focus:border-primary focus:ring-1 focus:ring-primary/40 text-left" |
|||
/> |
|||
</div> |
|||
</div> |
|||
|
|||
{/* آدرس دفتر مرکزی */} |
|||
<div className="space-y-1.5 md:col-span-2"> |
|||
<label className="text-xs font-semibold text-slate-300 flex items-center gap-1.5"> |
|||
<Ic name="mapPin" className="size-3.5 text-rose-400" /> |
|||
<span>{t('settings.addressLabel')}</span> |
|||
</label> |
|||
<textarea |
|||
rows={2} |
|||
value={contactus.address || ''} |
|||
onChange={(e) => setContactus((prev) => ({ ...prev, address: e.target.value }))} |
|||
placeholder="آدرس دقیق دفتر مرکزی عقیله جهت نمایش در بخش ارتباط با ما..." |
|||
className="w-full rounded-xl bg-[#14161c] border border-white/[0.1] p-3 text-xs font-medium text-slate-100 placeholder:text-slate-500 outline-none transition-colors focus:border-primary focus:ring-1 focus:ring-primary/40 resize-none" |
|||
/> |
|||
</div> |
|||
</div> |
|||
|
|||
<div className="flex justify-end pt-2 border-t border-white/[0.07]"> |
|||
<Button |
|||
type="button" |
|||
disabled={saving || loading} |
|||
onClick={handleSaveToDatabase} |
|||
className="gap-2 rounded-xl text-xs font-black bg-gradient-to-r from-[#d09460] to-[#b37745] hover:from-[#dd9e69] hover:to-[#be804c] text-white shadow-md shadow-[#d09460]/20 transition-all active:scale-[0.98]" |
|||
> |
|||
<Ic name="save" className={`size-4 ${saving ? 'animate-spin' : ''}`} /> |
|||
<span>{saving ? t('settings.saving') : t('settings.saveSocialBtn')}</span> |
|||
</Button> |
|||
</div> |
|||
</CardContent> |
|||
</Card> |
|||
|
|||
{/* مدال افزودن پلتفرم جدید (کالیبره تم تیره) */} |
|||
<Dialog open={addModalOpen} onOpenChange={setAddModalOpen}> |
|||
<DialogContent className="max-w-md bg-[#1e2129] border border-white/[0.12] text-white"> |
|||
<DialogHeader className="p-5 pb-0"> |
|||
<DialogTitle className="text-sm font-black text-white flex items-center gap-2"> |
|||
<div className="flex size-7 items-center justify-center rounded-lg bg-primary/15 text-primary"> |
|||
<Ic name="share" className="size-4" /> |
|||
</div> |
|||
<span>{t('settings.addSocialBtn')}</span> |
|||
</DialogTitle> |
|||
</DialogHeader> |
|||
|
|||
<div className="p-5 space-y-4"> |
|||
<div className="space-y-2"> |
|||
<label className="text-xs font-semibold text-slate-300"> |
|||
{t('settings.selectPlatform')} |
|||
</label> |
|||
<div className="grid grid-cols-3 sm:grid-cols-4 gap-2 max-h-52 overflow-y-auto p-1.5 border rounded-xl border-white/[0.08] bg-[#14161c] scrollbar-thin"> |
|||
{Object.values(PRESET_PLATFORMS).map((preset) => { |
|||
const isSelected = selectedPlatformKey === preset.key |
|||
return ( |
|||
<button |
|||
key={preset.key} |
|||
type="button" |
|||
onClick={() => handleSelectPreset(preset.key)} |
|||
className={`flex flex-col items-center justify-center p-2 rounded-xl border text-center transition-all ${ |
|||
isSelected |
|||
? 'border-primary bg-primary/20 text-primary shadow-sm' |
|||
: 'border-white/[0.06] bg-[#191b22] hover:bg-[#22252f] text-slate-300' |
|||
}`}
|
|||
> |
|||
<div |
|||
className="size-8 rounded-lg flex items-center justify-center mb-1.5 bg-white/[0.05] border border-white/[0.05]" |
|||
style={{ color: preset.brandColor }} |
|||
> |
|||
<Ic name={preset.icon} className="size-4.5" /> |
|||
</div> |
|||
<span className="text-[11px] font-bold truncate max-w-full"> |
|||
{preset.name} |
|||
</span> |
|||
</button> |
|||
) |
|||
})} |
|||
</div> |
|||
</div> |
|||
|
|||
<div className="space-y-1.5"> |
|||
<label className="text-xs font-semibold text-slate-300"> |
|||
{t('settings.channelName')} |
|||
</label> |
|||
<input |
|||
type="text" |
|||
value={newChannelName} |
|||
onChange={(e) => setNewChannelName(e.target.value)} |
|||
placeholder="عنوان نمایشی کانال یا حساب کاربری" |
|||
className="w-full h-10 rounded-xl bg-[#14161c] border border-white/[0.1] px-3.5 text-xs text-slate-100 placeholder:text-slate-500 outline-none focus:border-primary" |
|||
/> |
|||
</div> |
|||
|
|||
<div className="space-y-1.5"> |
|||
<label className="text-xs font-semibold text-slate-300"> |
|||
{t('settings.channelLink')} |
|||
</label> |
|||
<input |
|||
type="text" |
|||
dir="ltr" |
|||
value={newChannelLink} |
|||
onChange={(e) => setNewChannelLink(e.target.value)} |
|||
placeholder={PRESET_PLATFORMS[selectedPlatformKey]?.placeholder || 'https://...'} |
|||
className="w-full h-10 rounded-xl bg-[#14161c] border border-white/[0.1] px-3.5 text-xs font-mono text-slate-100 placeholder:text-slate-500 outline-none focus:border-primary text-left" |
|||
/> |
|||
</div> |
|||
</div> |
|||
|
|||
<DialogFooter className="px-5 py-4 border-t border-white/[0.08] flex items-center justify-end gap-2 bg-[#171920]"> |
|||
<Button |
|||
type="button" |
|||
variant="outline" |
|||
size="sm" |
|||
onClick={() => setAddModalOpen(false)} |
|||
className="rounded-xl text-xs border-white/10 hover:bg-white/[0.06] text-slate-300" |
|||
> |
|||
انصراف |
|||
</Button> |
|||
<Button |
|||
type="button" |
|||
size="sm" |
|||
onClick={handleAddChannel} |
|||
className="rounded-xl text-xs font-black bg-gradient-to-r from-[#d09460] to-[#b37745] hover:from-[#dd9e69] hover:to-[#be804c] text-white shadow-sm" |
|||
> |
|||
افزودن به لیست |
|||
</Button> |
|||
</DialogFooter> |
|||
</DialogContent> |
|||
</Dialog> |
|||
</div> |
|||
) |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue