feat: add superadmin tenant module

This commit is contained in:
2026-06-16 22:30:29 +05:30
parent 6c97f89b30
commit 6f4399a048
18 changed files with 1146 additions and 0 deletions

View File

@@ -0,0 +1,157 @@
'use client';
import { useCallback } from 'react';
import { useForm, useWatch } from 'react-hook-form';
import { toast } from 'sonner';
import type { Tenant } from '@/types';
import { useSaveTenantMutation } from './useTenantMutations';
export interface TenantFormValues {
id?: number;
name: string;
slug: string;
client_id: string;
plan_id: string;
domain: string;
description: string;
admin_first_name: string;
admin_last_name: string;
admin_email: string;
admin_phone_number: string;
}
const defaultValues: TenantFormValues = {
name: '',
slug: '',
client_id: '',
plan_id: '',
domain: '',
description: '',
admin_first_name: '',
admin_last_name: '',
admin_email: '',
admin_phone_number: '',
};
function createSlug(value: string) {
return value
.trim()
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '');
}
function splitContactName(contactName: string) {
const parts = contactName.trim().split(/\s+/).filter(Boolean);
if (parts.length === 0) return { firstName: '', lastName: '' };
if (parts.length === 1) return { firstName: parts[0], lastName: '' };
return { firstName: parts[0], lastName: parts.slice(1).join(' ') };
}
export function useTenantForm({ onSaved }: { onSaved: () => void }) {
const {
register,
handleSubmit: submitForm,
reset,
control,
setValue,
formState: { isSubmitting },
} = useForm<TenantFormValues>({
defaultValues,
});
const saveMutation = useSaveTenantMutation({
onSaved: () => {
reset(defaultValues);
onSaved();
},
});
const tenantId = useWatch({ control, name: 'id' });
const clientId = useWatch({ control, name: 'client_id' }) || '';
const planId = useWatch({ control, name: 'plan_id' }) || '';
const adminEmail = useWatch({ control, name: 'admin_email' }) || '';
const isEditMode = Boolean(tenantId);
const handleSubmit = submitForm(
(values) => {
if (!values.client_id || !values.plan_id) {
toast.error('Select a client and plan');
return;
}
if (!isEditMode) {
if (!values.admin_first_name.trim() || !values.admin_last_name.trim() || !values.admin_email.trim()) {
toast.error('Complete all required admin fields');
return;
}
}
saveMutation.mutate(values);
},
() => toast.error('Complete all required tenant fields'),
);
const openCreate = useCallback(() => {
reset(defaultValues);
}, [reset]);
const openEdit = useCallback(
(tenant: Tenant) => {
reset({
id: tenant.id,
name: tenant.name || '',
slug: tenant.slug || '',
client_id: String(tenant.client_id ?? ''),
plan_id: String(tenant.plan_id ?? ''),
domain: tenant.domain || '',
description: tenant.description || '',
admin_first_name: '',
admin_last_name: '',
admin_email: tenant.admin_email || '',
admin_phone_number: '',
});
},
[reset],
);
const applyClientAdminDefaults = useCallback(
(contactName: string, contactEmail: string, contactPhone: string) => {
if (isEditMode) return;
const { firstName, lastName } = splitContactName(contactName);
setValue('admin_first_name', firstName, { shouldDirty: true });
setValue('admin_last_name', lastName, { shouldDirty: true });
setValue('admin_email', contactEmail, { shouldDirty: true });
setValue('admin_phone_number', contactPhone, { shouldDirty: true });
},
[isEditMode, setValue],
);
const syncSlugFromName = useCallback(
(name: string) => {
if (isEditMode) return;
setValue('slug', createSlug(name), { shouldDirty: true });
},
[isEditMode, setValue],
);
return {
register,
handleSubmit,
reset,
control,
setValue,
openCreate,
openEdit,
tenantId,
clientId,
planId,
adminEmail,
isEditMode,
applyClientAdminDefaults,
syncSlugFromName,
isSaving: isSubmitting || saveMutation.isPending,
};
}