diff --git a/assets/admin/pages/AdminSubscriptionPage.tsx b/assets/admin/pages/AdminSubscriptionPage.tsx index 936e0bb9..6f7a5ace 100644 --- a/assets/admin/pages/AdminSubscriptionPage.tsx +++ b/assets/admin/pages/AdminSubscriptionPage.tsx @@ -40,7 +40,7 @@ const planSchema = z.object({ type PlanForm = z.infer; const periodSchema = z.object({ - plan_uuid: z.string().min(1, 'پنل الزامی است'), + plan_uuid: z.string().min(1, 'پلن الزامی است'), label: z.string().min(1, 'عنوان دوره الزامی است'), duration_months: z.coerce.number().min(1), price_rials: z.coerce.number().min(0), @@ -70,6 +70,19 @@ const PLAN_DISPLAY: Record = { professional: 'حرفه‌ای', }; +// ── Switch toggle ───────────────────────────────────────────────────────── + +const SwitchToggle = React.forwardRef>( + function SwitchToggle(props, ref) { + return ( + e.stopPropagation()}> + + + + ); + }, +); + // ── Plans tab ───────────────────────────────────────────────────────────── function PlansTab() { @@ -98,13 +111,13 @@ function PlansTab() { const createPlanMut = useMutation({ mutationFn: (body: PlanForm) => api.post('/api/v1/admin/subscription/plan', body), - onSuccess: () => { qc.invalidateQueries({ queryKey: ['admin-subscription-plans'] }); setPlanModal(null); planForm.reset(); toast.success('پنل ایجاد شد'); }, + onSuccess: () => { qc.invalidateQueries({ queryKey: ['admin-subscription-plans'] }); setPlanModal(null); planForm.reset(); toast.success('پلن ایجاد شد'); }, onError: (e: any) => toast.error(e.message), }); const updatePlanMut = useMutation({ mutationFn: ({ uuid, body }: { uuid: string; body: PlanForm }) => api.patch(`/api/v1/admin/subscription/plan/${uuid}`, body), - onSuccess: () => { qc.invalidateQueries({ queryKey: ['admin-subscription-plans'] }); setPlanModal(null); toast.success('پنل بروزرسانی شد'); }, + onSuccess: () => { qc.invalidateQueries({ queryKey: ['admin-subscription-plans'] }); setPlanModal(null); toast.success('پلن بروزرسانی شد'); }, onError: (e: any) => toast.error(e.message), }); @@ -160,7 +173,7 @@ function PlansTab() { <>
@@ -237,7 +250,7 @@ function PlansTab() { setPlanModal(null)} - title={planModal === 'create' ? 'پنل جدید' : 'ویرایش پنل'} + title={planModal === 'create' ? 'پلن جدید' : 'ویرایش پلن'} >
{ if (planModal === 'create') createPlanMut.mutate(d); @@ -259,20 +272,20 @@ function PlansTab() { -
- -
- {(Object.keys(FEATURE_LABELS) as (keyof typeof FEATURE_LABELS)[]).map((k) => ( -
@@ -373,7 +386,7 @@ function ReportTab() { نوع - پنل + پلن نوع اشتراک شروع انقضا @@ -420,10 +433,10 @@ export default function AdminSubscriptionPage() { return ( <> - +
- +
diff --git a/assets/admin/styles.css b/assets/admin/styles.css index 16e66c54..3d942af2 100644 --- a/assets/admin/styles.css +++ b/assets/admin/styles.css @@ -533,6 +533,22 @@ table.t tbody tr:hover .row-actions { opacity: 1; } .seg button { padding: 7px 14px; border-radius: 8px; font-size: 13px; font-weight: 600; color: var(--text-2); transition: .14s; cursor: pointer; font-family: inherit; } .seg button.on { background: var(--surface); color: var(--text); box-shadow: var(--shadow-sm); } +/* ── Switch toggle ───────────────────────────────────────────── */ +.switch { position: relative; display: inline-block; width: 44px; height: 24px; flex-shrink: 0; } +.switch input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; z-index: 2; } +.switch-track { + position: absolute; inset: 0; display: block; + background: var(--surface-2); border: 1px solid var(--border); border-radius: 999px; transition: background .18s, border-color .18s; +} +.switch-thumb { + position: absolute; top: 50%; right: 3px; width: 16px; height: 16px; margin-top: -8px; + border-radius: 50%; background: var(--text-3); box-shadow: var(--shadow-sm); + transition: transform .18s, background .18s; +} +.switch input:checked ~ .switch-track { background: var(--primary); border-color: var(--primary); } +.switch input:checked ~ .switch-track .switch-thumb { background: #fff; transform: translateX(-20px); } +.switch input:focus-visible ~ .switch-track { box-shadow: 0 0 0 4px var(--ring); } + /* ── Pagination ──────────────────────────────────────────────── */ .pagination { display: flex; align-items: center; gap: 6px; padding: 16px 0;