diff --git a/packages/admin/src/api/accounts.ts b/packages/admin/src/api/accounts.ts new file mode 100644 index 0000000..eb15919 --- /dev/null +++ b/packages/admin/src/api/accounts.ts @@ -0,0 +1,37 @@ +import { queryOptions } from '@tanstack/react-query' +import { api } from '@/lib/api-client' +import type { Account } from '@/types/account' +import type { PaginatedResponse, PaginationInput } from '@forte/shared/schemas' + +export const accountKeys = { + all: ['accounts'] as const, + lists: () => [...accountKeys.all, 'list'] as const, + list: (params: PaginationInput) => [...accountKeys.all, 'list', params] as const, + details: () => [...accountKeys.all, 'detail'] as const, + detail: (id: string) => [...accountKeys.all, 'detail', id] as const, +} + +export function accountListOptions(params: PaginationInput) { + return queryOptions({ + queryKey: accountKeys.list(params), + queryFn: () => api.get>('/v1/accounts', params), + }) +} + +export function accountDetailOptions(id: string) { + return queryOptions({ + queryKey: accountKeys.detail(id), + queryFn: () => api.get(`/v1/accounts/${id}`), + }) +} + +export const accountMutations = { + create: (data: Record) => + api.post('/v1/accounts', data), + + update: (id: string, data: Record) => + api.patch(`/v1/accounts/${id}`, data), + + delete: (id: string) => + api.del(`/v1/accounts/${id}`), +} diff --git a/packages/admin/src/api/members.ts b/packages/admin/src/api/members.ts new file mode 100644 index 0000000..0626ce5 --- /dev/null +++ b/packages/admin/src/api/members.ts @@ -0,0 +1,27 @@ +import { queryOptions } from '@tanstack/react-query' +import { api } from '@/lib/api-client' +import type { Member } from '@/types/account' +import type { PaginatedResponse, PaginationInput } from '@forte/shared/schemas' + +export const memberKeys = { + all: (accountId: string) => ['accounts', accountId, 'members'] as const, + list: (accountId: string, params: PaginationInput) => [...memberKeys.all(accountId), params] as const, +} + +export function memberListOptions(accountId: string, params: PaginationInput) { + return queryOptions({ + queryKey: memberKeys.list(accountId, params), + queryFn: () => api.get>(`/v1/accounts/${accountId}/members`, params), + }) +} + +export const memberMutations = { + create: (accountId: string, data: Record) => + api.post(`/v1/accounts/${accountId}/members`, data), + + update: (id: string, data: Record) => + api.patch(`/v1/members/${id}`, data), + + delete: (id: string) => + api.del(`/v1/members/${id}`), +} diff --git a/packages/admin/src/api/payment-methods.ts b/packages/admin/src/api/payment-methods.ts new file mode 100644 index 0000000..218cc96 --- /dev/null +++ b/packages/admin/src/api/payment-methods.ts @@ -0,0 +1,25 @@ +import { queryOptions } from '@tanstack/react-query' +import { api } from '@/lib/api-client' +import type { PaymentMethod } from '@/types/account' + +export const paymentMethodKeys = { + all: (accountId: string) => ['accounts', accountId, 'payment-methods'] as const, +} + +export function paymentMethodListOptions(accountId: string) { + return queryOptions({ + queryKey: paymentMethodKeys.all(accountId), + queryFn: () => api.get<{ data: PaymentMethod[] }>(`/v1/accounts/${accountId}/payment-methods`), + }) +} + +export const paymentMethodMutations = { + create: (accountId: string, data: Record) => + api.post(`/v1/accounts/${accountId}/payment-methods`, data), + + update: (id: string, data: Record) => + api.patch(`/v1/payment-methods/${id}`, data), + + delete: (id: string) => + api.del(`/v1/payment-methods/${id}`), +} diff --git a/packages/admin/src/api/processor-links.ts b/packages/admin/src/api/processor-links.ts new file mode 100644 index 0000000..316ea56 --- /dev/null +++ b/packages/admin/src/api/processor-links.ts @@ -0,0 +1,25 @@ +import { queryOptions } from '@tanstack/react-query' +import { api } from '@/lib/api-client' +import type { ProcessorLink } from '@/types/account' + +export const processorLinkKeys = { + all: (accountId: string) => ['accounts', accountId, 'processor-links'] as const, +} + +export function processorLinkListOptions(accountId: string) { + return queryOptions({ + queryKey: processorLinkKeys.all(accountId), + queryFn: () => api.get<{ data: ProcessorLink[] }>(`/v1/accounts/${accountId}/processor-links`), + }) +} + +export const processorLinkMutations = { + create: (accountId: string, data: Record) => + api.post(`/v1/accounts/${accountId}/processor-links`, data), + + update: (id: string, data: Record) => + api.patch(`/v1/processor-links/${id}`, data), + + delete: (id: string) => + api.del(`/v1/processor-links/${id}`), +} diff --git a/packages/admin/src/api/tax-exemptions.ts b/packages/admin/src/api/tax-exemptions.ts new file mode 100644 index 0000000..713a98c --- /dev/null +++ b/packages/admin/src/api/tax-exemptions.ts @@ -0,0 +1,28 @@ +import { queryOptions } from '@tanstack/react-query' +import { api } from '@/lib/api-client' +import type { TaxExemption } from '@/types/account' + +export const taxExemptionKeys = { + all: (accountId: string) => ['accounts', accountId, 'tax-exemptions'] as const, +} + +export function taxExemptionListOptions(accountId: string) { + return queryOptions({ + queryKey: taxExemptionKeys.all(accountId), + queryFn: () => api.get<{ data: TaxExemption[] }>(`/v1/accounts/${accountId}/tax-exemptions`), + }) +} + +export const taxExemptionMutations = { + create: (accountId: string, data: Record) => + api.post(`/v1/accounts/${accountId}/tax-exemptions`, data), + + update: (id: string, data: Record) => + api.patch(`/v1/tax-exemptions/${id}`, data), + + approve: (id: string) => + api.post(`/v1/tax-exemptions/${id}/approve`, {}), + + revoke: (id: string, reason: string) => + api.post(`/v1/tax-exemptions/${id}/revoke`, { reason }), +} diff --git a/packages/admin/src/components/accounts/account-form.tsx b/packages/admin/src/components/accounts/account-form.tsx new file mode 100644 index 0000000..72f4132 --- /dev/null +++ b/packages/admin/src/components/accounts/account-form.tsx @@ -0,0 +1,96 @@ +import { useForm } from 'react-hook-form' +import { zodResolver } from '@hookform/resolvers/zod' +import { AccountCreateSchema } from '@forte/shared/schemas' +import type { AccountCreateInput } from '@forte/shared/schemas' +import { Button } from '@/components/ui/button' +import { Input } from '@/components/ui/input' +import { Label } from '@/components/ui/label' +import { Textarea } from '@/components/ui/textarea' +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' +import type { Account } from '@/types/account' + +interface AccountFormProps { + defaultValues?: Partial + onSubmit: (data: AccountCreateInput) => void + loading?: boolean +} + +export function AccountForm({ defaultValues, onSubmit, loading }: AccountFormProps) { + const { + register, + handleSubmit, + setValue, + watch, + formState: { errors }, + } = useForm({ + resolver: zodResolver(AccountCreateSchema), + defaultValues: { + name: defaultValues?.name ?? '', + email: defaultValues?.email ?? undefined, + phone: defaultValues?.phone ?? undefined, + billingMode: defaultValues?.billingMode ?? 'consolidated', + notes: defaultValues?.notes ?? undefined, + address: defaultValues?.address ?? undefined, + }, + }) + + const billingMode = watch('billingMode') + + return ( +
+
+ + + {errors.name &&

{errors.name.message}

} +
+ +
+
+ + +
+
+ + +
+
+ +
+ + +
+ +
+ +
+ + +
+ + +
+
+
+ +
+ +