feat: port wallet charge/withdraw modal from tauri to patient admin page
Add manual wallet withdrawal (debit) endpoint mirroring the offline app's
balance guard, and rebuild the patient کیف پول tab around a single
charge/withdraw toggle modal (quick amounts, تومان→ریال conversion,
transaction filters).
Backend:
- POST /api/v1/patient/{uuid}/wallet/withdraw — creates a debit
WalletTransaction; 422 ERR_WALLET_INSUFFICIENT when amount exceeds balance.
- ErrorCodes: ERR_WALLET_INSUFFICIENT ('موجودی کیف پول کافی نیست').
- docs/api/patient.md updated.
Frontend:
- usePatientWallet hook (balance + charge/withdraw mutations).
- WalletTransactionModal (toggle, quick amounts, UI-only payment fields).
- WalletTab: charge button, همه/واریزی/برداشت filters.
Tests: backend withdraw success/insufficient/non-positive/ownership;
frontend modal + wallet tab interactions.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { api } from '../lib/api';
|
||||
import type { ApiResponse } from '../lib/api';
|
||||
|
||||
export interface WalletTxn {
|
||||
uuid: string;
|
||||
amount_rials: number;
|
||||
type: 'credit' | 'debit' | string;
|
||||
description?: string | null;
|
||||
balance_after: number;
|
||||
created_at: number;
|
||||
}
|
||||
|
||||
interface WalletData {
|
||||
balance_rials: number;
|
||||
recent_transactions: WalletTxn[];
|
||||
}
|
||||
|
||||
export interface WalletTxnInput {
|
||||
amount_rials: number;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* کیف پول بیمار: موجودی + تراکنشهای اخیر، و mutationهای شارژ (credit) و برداشت (debit).
|
||||
* منبع: GET /api/v1/patient/{uuid}/wallet و POST .../wallet/{charge|withdraw}.
|
||||
* کلید کوئری: ['patient-wallet', uuid]؛ پس از هر mutation موفق invalidate میشود.
|
||||
*/
|
||||
export function usePatientWallet(uuid: string | undefined) {
|
||||
const qc = useQueryClient();
|
||||
const queryKey = ['patient-wallet', uuid];
|
||||
|
||||
const query = useQuery<ApiResponse<WalletData>>({
|
||||
queryKey,
|
||||
queryFn: () => api.get(`/api/v1/patient/${uuid}/wallet`),
|
||||
enabled: !!uuid,
|
||||
});
|
||||
|
||||
const invalidate = () => qc.invalidateQueries({ queryKey });
|
||||
|
||||
const charge = useMutation<ApiResponse<unknown>, unknown, WalletTxnInput>({
|
||||
mutationFn: (body) => api.post(`/api/v1/patient/${uuid}/wallet/charge`, body),
|
||||
onSuccess: invalidate,
|
||||
});
|
||||
|
||||
const withdraw = useMutation<ApiResponse<unknown>, unknown, WalletTxnInput>({
|
||||
mutationFn: (body) => api.post(`/api/v1/patient/${uuid}/wallet/withdraw`, body),
|
||||
onSuccess: invalidate,
|
||||
});
|
||||
|
||||
return {
|
||||
balanceRials: query.data?.data?.balance_rials ?? 0,
|
||||
transactions: query.data?.data?.recent_transactions ?? [],
|
||||
isLoading: query.isLoading,
|
||||
charge,
|
||||
withdraw,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user