Extract admin dashboard from ghabilee-frontend2 into a dedicated Next.js app for backoffice.ghabilee.ir (no SEO indexing / Clarity).
104 lines
3.0 KiB
TypeScript
104 lines
3.0 KiB
TypeScript
'use client'
|
||
|
||
import { zodResolver } from '@hookform/resolvers/zod'
|
||
import { useEffect, useState } from 'react'
|
||
import { FormProvider, useForm } from 'react-hook-form'
|
||
|
||
import { addToast } from '@/lib/toast'
|
||
import type { AdminUserDetail } from '@/services/adminUserDetail'
|
||
import Modal from '@/components/modals/Modal'
|
||
import Input from '@/components/formElements/Input'
|
||
import { formatToman } from '@/features/admin-users/adminUserDetailUi'
|
||
import { ADMIN_CREDIT_WALLET } from '@/services/adminWallet'
|
||
import { AdminWalletCreditValidation, type AdminWalletCreditValues } from '@/validation/adminWalletCredit'
|
||
|
||
const DEFAULT_VALUES: AdminWalletCreditValues = {
|
||
amount: '',
|
||
description: '',
|
||
}
|
||
|
||
interface WalletCreditModalProps {
|
||
isOpen: boolean
|
||
onOpenChange: (isOpen: boolean) => void
|
||
user: AdminUserDetail
|
||
onSuccess: () => void
|
||
}
|
||
|
||
const WalletCreditModal = ({ isOpen, onOpenChange, user, onSuccess }: WalletCreditModalProps) => {
|
||
const [submitting, setSubmitting] = useState(false)
|
||
|
||
const form = useForm<AdminWalletCreditValues>({
|
||
resolver: zodResolver(AdminWalletCreditValidation),
|
||
defaultValues: DEFAULT_VALUES,
|
||
})
|
||
const { reset } = form
|
||
|
||
useEffect(() => {
|
||
if (isOpen) {
|
||
reset(DEFAULT_VALUES)
|
||
}
|
||
}, [isOpen, reset])
|
||
|
||
const handleSubmit = async (values: AdminWalletCreditValues) => {
|
||
const description = values.description?.trim()
|
||
const payload = {
|
||
amount: Number(values.amount),
|
||
...(description ? { description } : {}),
|
||
}
|
||
|
||
setSubmitting(true)
|
||
const result = await ADMIN_CREDIT_WALLET(user.id, payload)
|
||
|
||
setSubmitting(false)
|
||
|
||
if (!result.ok) return
|
||
|
||
addToast({
|
||
title: `کیفپول شارژ شد · موجودی جدید: ${formatToman(result.data.walletBalance)}`,
|
||
color: 'success',
|
||
})
|
||
onOpenChange(false)
|
||
onSuccess()
|
||
}
|
||
|
||
const displayName = [user.firstName, user.lastName].filter(Boolean).join(' ') || user.mobile
|
||
|
||
return (
|
||
<Modal
|
||
acceptBtnText="شارژ کیفپول"
|
||
isLoading={submitting}
|
||
isOpen={isOpen}
|
||
title={`شارژ کیفپول «${displayName}»`}
|
||
onAccept={form.handleSubmit(handleSubmit)}
|
||
onOpenChange={onOpenChange}
|
||
>
|
||
<FormProvider {...form}>
|
||
<form
|
||
className="flex flex-col gap-4"
|
||
onSubmit={form.handleSubmit(handleSubmit)}
|
||
>
|
||
<p className="text-sm text-text-muted">
|
||
موجودی فعلی: <span className="font-medium text-text-dark">{formatToman(user.walletBalance)}</span>
|
||
</p>
|
||
<Input
|
||
englishDigitsOnly
|
||
direction="ltr"
|
||
generalType="input"
|
||
label="مبلغ (تومان)"
|
||
name="amount"
|
||
placeholder="مثلاً ۱۰۰۰۰۰"
|
||
/>
|
||
<Input
|
||
generalType="textarea"
|
||
label="توضیحات (اختیاری)"
|
||
name="description"
|
||
placeholder="حداقل ۳ کاراکتر در صورت وارد کردن"
|
||
/>
|
||
</form>
|
||
</FormProvider>
|
||
</Modal>
|
||
)
|
||
}
|
||
|
||
export default WalletCreditModal
|