feat: restructure frontend routes — vendor/tenant persona split
Splits the flat 3-page UI into /vendor/* (platform:admin) and /tenant/* (all authenticated users) route trees, with stub pages, new API hooks, updated Layout with persona-aware sidebar, and SpaController forwarding. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
63
ui/src/api/tenant-hooks.ts
Normal file
63
ui/src/api/tenant-hooks.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { api } from './client';
|
||||
import type { DashboardData, TenantLicenseData, TenantSettings } from '../types/api';
|
||||
|
||||
export function useTenantDashboard() {
|
||||
return useQuery<DashboardData>({
|
||||
queryKey: ['tenant', 'dashboard'],
|
||||
queryFn: () => api.get('/tenant/dashboard'),
|
||||
refetchInterval: 30_000,
|
||||
});
|
||||
}
|
||||
|
||||
export function useTenantLicense() {
|
||||
return useQuery<TenantLicenseData>({
|
||||
queryKey: ['tenant', 'license'],
|
||||
queryFn: () => api.get('/tenant/license'),
|
||||
});
|
||||
}
|
||||
|
||||
export function useTenantOidc() {
|
||||
return useQuery<Record<string, unknown>>({
|
||||
queryKey: ['tenant', 'oidc'],
|
||||
queryFn: () => api.get('/tenant/oidc'),
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateOidc() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation<void, Error, Record<string, unknown>>({
|
||||
mutationFn: (config) => api.post('/tenant/oidc', config),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['tenant', 'oidc'] }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useTenantTeam() {
|
||||
return useQuery<Array<Record<string, unknown>>>({
|
||||
queryKey: ['tenant', 'team'],
|
||||
queryFn: () => api.get('/tenant/team'),
|
||||
});
|
||||
}
|
||||
|
||||
export function useInviteTeamMember() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation<{ userId: string }, Error, { email: string; roleId: string }>({
|
||||
mutationFn: (body) => api.post('/tenant/team/invite', body),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['tenant', 'team'] }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useRemoveTeamMember() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation<void, Error, string>({
|
||||
mutationFn: (userId) => api.delete(`/tenant/team/${userId}`),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['tenant', 'team'] }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useTenantSettings() {
|
||||
return useQuery<TenantSettings>({
|
||||
queryKey: ['tenant', 'settings'],
|
||||
queryFn: () => api.get('/tenant/settings'),
|
||||
});
|
||||
}
|
||||
59
ui/src/api/vendor-hooks.ts
Normal file
59
ui/src/api/vendor-hooks.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { api } from './client';
|
||||
import type { VendorTenantSummary, VendorTenantDetail, CreateTenantRequest, TenantResponse, LicenseResponse } from '../types/api';
|
||||
|
||||
export function useVendorTenants() {
|
||||
return useQuery<VendorTenantSummary[]>({
|
||||
queryKey: ['vendor', 'tenants'],
|
||||
queryFn: () => api.get('/vendor/tenants'),
|
||||
refetchInterval: 30_000,
|
||||
});
|
||||
}
|
||||
|
||||
export function useVendorTenant(id: string | null) {
|
||||
return useQuery<VendorTenantDetail>({
|
||||
queryKey: ['vendor', 'tenants', id],
|
||||
queryFn: () => api.get(`/vendor/tenants/${id}`),
|
||||
enabled: !!id,
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateTenant() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation<TenantResponse, Error, CreateTenantRequest>({
|
||||
mutationFn: (req) => api.post('/vendor/tenants', req),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['vendor', 'tenants'] }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useSuspendTenant() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation<TenantResponse, Error, string>({
|
||||
mutationFn: (id) => api.post(`/vendor/tenants/${id}/suspend`),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['vendor', 'tenants'] }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useActivateTenant() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation<TenantResponse, Error, string>({
|
||||
mutationFn: (id) => api.post(`/vendor/tenants/${id}/activate`),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['vendor', 'tenants'] }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteTenant() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation<void, Error, string>({
|
||||
mutationFn: (id) => api.delete(`/vendor/tenants/${id}`),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['vendor', 'tenants'] }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useRenewLicense() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation<LicenseResponse, Error, string>({
|
||||
mutationFn: (tenantId) => api.post(`/vendor/tenants/${tenantId}/license`),
|
||||
onSuccess: (_, tenantId) => qc.invalidateQueries({ queryKey: ['vendor', 'tenants', tenantId] }),
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user