import React, { useEffect, useState } from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { useForm } from 'react-hook-form'; import { z } from 'zod'; import { zodResolver } from '@hookform/resolvers/zod'; import { api } from '../lib/api'; import type { ApiResponse } from '../lib/api'; import { formatDateTime } from '../lib/utils'; import { Cog6ToothIcon, CheckCircleIcon } from '@heroicons/react/24/outline'; interface TaxHistoryRow { tax_percent: number; enabled: boolean; changed_by_name: string | null; changed_at: number; } // ── Schema ──────────────────────────────────────────────────────────────── const schema = z.object({ site_name: z.string().min(1, 'نام سایت الزامی است'), support_phone: z.string(), max_cancel_hours_before: z.string(), appointment_reminder_hours: z.string(), // financial engine appointment_commission_enabled: z.string(), upgrade_commission_enabled: z.string(), upgrade_commission_percent: z.string(), tax_enabled: z.string(), tax_percent: z.string(), sms_panel_fee_rials: z.string(), appointment_fee_rials: z.string(), // payment gateways payment_test_mode: z.string(), mellat_terminal_id: z.string(), mellat_username: z.string(), mellat_password: z.string(), sep_terminal_id: z.string(), // sms sms_provider: z.string(), kavenegar_api_key: z.string(), kavenegar_sender: z.string(), sms_price_rials: z.string(), }); type FormValues = z.infer; interface Settings { site_name: string; support_phone: string; max_cancel_hours_before: string; appointment_reminder_hours: string; appointment_commission_enabled: string; upgrade_commission_enabled: string; upgrade_commission_percent: string; tax_enabled: string; tax_percent: string; sms_panel_fee_rials: string; appointment_fee_rials: string; payment_test_mode: string; mellat_terminal_id: string; mellat_username: string; mellat_password: string; sep_terminal_id: string; sms_provider: string; kavenegar_api_key: string; kavenegar_sender: string; sms_price_rials: string; } // ── Component ───────────────────────────────────────────────────────────── export default function SettingsPage() { const qc = useQueryClient(); const [showMellatPassword, setShowMellatPassword] = useState(false); const [showKavenegarKey, setShowKavenegarKey] = useState(false); const { data, isLoading } = useQuery({ queryKey: ['admin-settings'], queryFn: () => api.get>('/api/v1/admin/settings'), staleTime: 30_000, }); // eslint-disable-next-line @typescript-eslint/no-explicit-any const settings: Settings | undefined = (data?.data as any)?.data ?? data?.data; const taxHistoryQ = useQuery({ queryKey: ['tax-history'], queryFn: () => api.get>('/api/v1/admin/settings/tax-history'), staleTime: 30_000, }); // eslint-disable-next-line @typescript-eslint/no-explicit-any const taxHistory: TaxHistoryRow[] = (taxHistoryQ.data?.data as any)?.data ?? taxHistoryQ.data?.data ?? []; const { register, handleSubmit, reset, watch, setValue, formState: { errors, isDirty }, } = useForm({ resolver: zodResolver(schema) }); useEffect(() => { if (settings) { reset({ site_name: settings.site_name ?? 'ClinicPro', support_phone: settings.support_phone ?? '', max_cancel_hours_before: settings.max_cancel_hours_before ?? '24', appointment_reminder_hours: settings.appointment_reminder_hours ?? '2', appointment_commission_enabled: settings.appointment_commission_enabled ?? '0', upgrade_commission_enabled: settings.upgrade_commission_enabled ?? '0', upgrade_commission_percent: settings.upgrade_commission_percent ?? '20', tax_enabled: settings.tax_enabled ?? '0', tax_percent: settings.tax_percent ?? '10', sms_panel_fee_rials: settings.sms_panel_fee_rials ?? '1500000', appointment_fee_rials: settings.appointment_fee_rials ?? '150000', payment_test_mode: settings.payment_test_mode ?? '0', mellat_terminal_id: settings.mellat_terminal_id ?? '', mellat_username: settings.mellat_username ?? '', mellat_password: settings.mellat_password ?? '', sep_terminal_id: settings.sep_terminal_id ?? '', sms_provider: settings.sms_provider ?? 'kavenegar', kavenegar_api_key: settings.kavenegar_api_key ?? '', kavenegar_sender: settings.kavenegar_sender ?? '', sms_price_rials: settings.sms_price_rials ?? '500', }); } }, [settings, reset]); const mutation = useMutation({ mutationFn: (values: FormValues) => api.patch>('/api/v1/admin/settings', values), onSuccess: () => { qc.invalidateQueries({ queryKey: ['admin-settings'] }); qc.invalidateQueries({ queryKey: ['tax-history'] }); }, }); const paymentTestMode = watch('payment_test_mode') === '1'; const apptCommissionEnabled = watch('appointment_commission_enabled') === '1'; const upgradeCommissionEnabled = watch('upgrade_commission_enabled') === '1'; const taxEnabled = watch('tax_enabled') === '1'; const onSubmit = (values: FormValues) => { mutation.mutate(values); }; if (isLoading) { return (
{Array.from({ length: 3 }).map((_, i) => (
))}
); } return (

تنظیمات سایت

پیکربندی کلی پلتفرم
{mutation.isSuccess && (
تنظیمات ذخیره شد
)}
{/* اطلاعات پایه */}

اطلاعات پایه

{errors.site_name &&

{errors.site_name.message}

}
{/* موتور مالی نمایندگی */}
🧮

موتور مالی نمایندگی

ترتیب کسرها: ابتدا هزینه پنل پیامک، سپس مالیات بر ارزش افزوده (استخراجی از مبلغ شامل مالیات)، و در نهایت پورسانت نماینده از مبلغِ خالصِ پس از مالیات محاسبه می‌شود.

{/* پورسانت نوبت */}