184 lines
8.5 KiB
TypeScript
184 lines
8.5 KiB
TypeScript
import { useState, useEffect } from 'react';
|
||
import { Settings as SettingsIcon, Save, Truck, ShieldAlert, Percent, AlertCircle } from 'lucide-react';
|
||
import api from '../services/api';
|
||
import Spinner from '../components/ui/Spinner';
|
||
|
||
export default function Settings() {
|
||
const [settings, setSettings] = useState({
|
||
SHIPPING_FEE: '',
|
||
MIN_ORDER_AMOUNT: '',
|
||
B2B_DISCOUNT_PERCENT: '',
|
||
MAINTENANCE_MODE: 'false',
|
||
CHARITY_ROUND_STEP: '10000',
|
||
});
|
||
const [isLoading, setIsLoading] = useState(true);
|
||
const [isSaving, setIsSaving] = useState(false);
|
||
|
||
const fetchSettings = async () => {
|
||
try {
|
||
setIsLoading(true);
|
||
const response = await api.get('/admin/settings');
|
||
if (response.data?.success) {
|
||
setSettings({
|
||
SHIPPING_FEE: response.data.data.SHIPPING_FEE || '0',
|
||
MIN_ORDER_AMOUNT: response.data.data.MIN_ORDER_AMOUNT || '0',
|
||
B2B_DISCOUNT_PERCENT: response.data.data.B2B_DISCOUNT_PERCENT || '0',
|
||
MAINTENANCE_MODE: response.data.data.MAINTENANCE_MODE || 'false',
|
||
CHARITY_ROUND_STEP: response.data.data.CHARITY_ROUND_STEP || '10000',
|
||
});
|
||
}
|
||
} catch (err) {
|
||
console.error('Failed to fetch settings', err);
|
||
} finally {
|
||
setIsLoading(false);
|
||
}
|
||
};
|
||
|
||
useEffect(() => {
|
||
fetchSettings();
|
||
}, []);
|
||
|
||
const handleSave = async (e: React.FormEvent) => {
|
||
e.preventDefault();
|
||
try {
|
||
setIsSaving(true);
|
||
await api.put('/admin/settings', settings);
|
||
alert('تنظیمات با موفقیت بروزرسانی شد.');
|
||
} catch (err) {
|
||
console.error('Failed to update settings', err);
|
||
} finally {
|
||
setIsSaving(false);
|
||
}
|
||
};
|
||
|
||
return (
|
||
<div className="space-y-6">
|
||
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
|
||
<div>
|
||
<h2 className="text-2xl font-black text-gray-900 flex items-center gap-2">
|
||
<SettingsIcon className="w-6 h-6 text-purple-600" />
|
||
تنظیمات فروشگاه
|
||
</h2>
|
||
<p className="text-gray-500 font-medium mt-1">مدیریت پارامترهای اصلی و سیستمی کانینا</p>
|
||
</div>
|
||
</div>
|
||
|
||
{isLoading ? (
|
||
<div className="flex justify-center p-12"><Spinner size="lg" className="text-purple-600" /></div>
|
||
) : (
|
||
<form onSubmit={handleSave} className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||
|
||
{/* Financial Settings */}
|
||
<div className="bg-white p-6 rounded-2xl shadow-sm border border-gray-200 space-y-6">
|
||
<h3 className="text-lg font-bold text-gray-900 border-b border-gray-100 pb-4 flex items-center gap-2">
|
||
<Truck className="w-5 h-5 text-blue-500" />
|
||
تنظیمات مالی و ارسال
|
||
</h3>
|
||
|
||
<div className="space-y-5">
|
||
<div>
|
||
<label className="block text-sm font-bold text-gray-700 mb-2">هزینه ثابت ارسال (تومان)</label>
|
||
<input
|
||
type="number"
|
||
value={settings.SHIPPING_FEE}
|
||
onChange={(e) => setSettings({ ...settings, SHIPPING_FEE: e.target.value })}
|
||
className="w-full border border-gray-200 rounded-xl p-3 outline-none focus:ring-2 focus:ring-purple-500 font-bold"
|
||
dir="ltr"
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-bold text-gray-700 mb-2">حداقل مبلغ سفارش (تومان)</label>
|
||
<input
|
||
type="number"
|
||
value={settings.MIN_ORDER_AMOUNT}
|
||
onChange={(e) => setSettings({ ...settings, MIN_ORDER_AMOUNT: e.target.value })}
|
||
className="w-full border border-gray-200 rounded-xl p-3 outline-none focus:ring-2 focus:ring-purple-500 font-bold"
|
||
dir="ltr"
|
||
/>
|
||
<p className="text-xs text-gray-500 mt-2 font-medium">سفارشات زیر این مبلغ امکان ثبت نخواهند داشت (۰ = بدون محدودیت).</p>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-bold text-gray-700 mb-2">پله رند کردن ردپای مهربانی (تومان)</label>
|
||
<input
|
||
type="number"
|
||
step="1000"
|
||
value={settings.CHARITY_ROUND_STEP}
|
||
onChange={(e) => setSettings({ ...settings, CHARITY_ROUND_STEP: e.target.value })}
|
||
className="w-full border border-gray-200 rounded-xl p-3 outline-none focus:ring-2 focus:ring-purple-500 font-bold"
|
||
dir="ltr"
|
||
placeholder="10000"
|
||
/>
|
||
<p className="text-xs text-gray-500 mt-2 font-medium">مبلغ فاکتور به اولین ضریب این عدد (مثلاً ۱۰,۰۰۰ یا ۵۰,۰۰۰ تومان) رند میشود.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* B2B Settings */}
|
||
<div className="bg-white p-6 rounded-2xl shadow-sm border border-gray-200 space-y-6">
|
||
<h3 className="text-lg font-bold text-gray-900 border-b border-gray-100 pb-4 flex items-center gap-2">
|
||
<Percent className="w-5 h-5 text-green-500" />
|
||
همکاران B2B
|
||
</h3>
|
||
|
||
<div className="space-y-5">
|
||
<div>
|
||
<label className="block text-sm font-bold text-gray-700 mb-2">تخفیف پیشفرض همکار (درصد)</label>
|
||
<input
|
||
type="number"
|
||
value={settings.B2B_DISCOUNT_PERCENT}
|
||
onChange={(e) => setSettings({ ...settings, B2B_DISCOUNT_PERCENT: e.target.value })}
|
||
className="w-full border border-gray-200 rounded-xl p-3 outline-none focus:ring-2 focus:ring-purple-500 font-bold"
|
||
dir="ltr"
|
||
max="100"
|
||
min="0"
|
||
/>
|
||
<p className="text-xs text-gray-500 mt-2 font-medium">این درصد به صورت خودکار روی قیمت تمام محصولات برای کاربران تایید شده B2B اعمال میشود.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* System Settings */}
|
||
<div className="bg-white p-6 rounded-2xl shadow-sm border border-gray-200 space-y-6 lg:col-span-2">
|
||
<h3 className="text-lg font-bold text-gray-900 border-b border-gray-100 pb-4 flex items-center gap-2">
|
||
<ShieldAlert className="w-5 h-5 text-orange-500" />
|
||
وضعیت سیستم
|
||
</h3>
|
||
|
||
<div className="flex items-center justify-between p-4 bg-orange-50 rounded-xl border border-orange-100">
|
||
<div className="flex items-center gap-3">
|
||
<AlertCircle className="w-6 h-6 text-orange-500" />
|
||
<div>
|
||
<h4 className="font-bold text-gray-900">حالت تعمیرات (Maintenance Mode)</h4>
|
||
<p className="text-sm text-gray-600 mt-1">با فعال کردن این گزینه، سایت برای کاربران از دسترس خارج شده و پیام «در حال بروزرسانی» نمایش داده میشود.</p>
|
||
</div>
|
||
</div>
|
||
<label className="relative inline-flex items-center cursor-pointer">
|
||
<input
|
||
type="checkbox"
|
||
className="sr-only peer"
|
||
checked={settings.MAINTENANCE_MODE === 'true'}
|
||
onChange={(e) => setSettings({ ...settings, MAINTENANCE_MODE: e.target.checked ? 'true' : 'false' })}
|
||
/>
|
||
<div className="w-14 h-7 bg-gray-200 peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-6 after:w-6 after:transition-all peer-checked:bg-orange-500"></div>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="lg:col-span-2 flex justify-end">
|
||
<button
|
||
type="submit"
|
||
disabled={isSaving}
|
||
className="bg-purple-600 hover:bg-purple-700 text-white font-bold py-3 px-8 rounded-xl transition-colors flex items-center gap-2 shadow-md shadow-purple-200"
|
||
>
|
||
{isSaving ? <Spinner size="sm" /> : <Save className="w-5 h-5" />}
|
||
ذخیره تغییرات
|
||
</button>
|
||
</div>
|
||
</form>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|