feat: vendor admin management and shared account settings #59
100
ui/src/api/account-hooks.ts
Normal file
100
ui/src/api/account-hooks.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { api } from './client';
|
||||
import type { AccountProfile, MfaStatus, MfaSetupResponse, BackupCodesResponse, PasskeyCredential } from '../types/api';
|
||||
|
||||
// --- Profile ---
|
||||
|
||||
export function useAccountProfile() {
|
||||
return useQuery<AccountProfile>({
|
||||
queryKey: ['account', 'profile'],
|
||||
queryFn: () => api.get('/account/profile'),
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateDisplayName() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation<void, Error, string>({
|
||||
mutationFn: (name) => api.patch('/account/profile', { name }),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['account', 'profile'] }),
|
||||
});
|
||||
}
|
||||
|
||||
// --- Password ---
|
||||
|
||||
export function useChangePassword() {
|
||||
return useMutation<void, Error, { currentPassword: string; newPassword: string }>({
|
||||
mutationFn: (body) => api.post('/account/password', body),
|
||||
});
|
||||
}
|
||||
|
||||
// --- MFA ---
|
||||
|
||||
export function useAccountMfaStatus() {
|
||||
return useQuery<MfaStatus>({
|
||||
queryKey: ['account', 'mfa', 'status'],
|
||||
queryFn: () => api.get('/account/mfa/status'),
|
||||
});
|
||||
}
|
||||
|
||||
export function useAccountMfaSetup() {
|
||||
return useMutation<MfaSetupResponse, Error, void>({
|
||||
mutationFn: () => api.post('/account/mfa/totp/setup'),
|
||||
});
|
||||
}
|
||||
|
||||
export function useAccountMfaVerify() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation<{ verified: boolean }, Error, { secret: string; code: string }>({
|
||||
mutationFn: (body) => api.post('/account/mfa/totp/verify', body),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['account', 'mfa'] }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useAccountBackupCodes() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation<BackupCodesResponse, Error, void>({
|
||||
mutationFn: () => api.post('/account/mfa/backup-codes'),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['account', 'mfa'] }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useAccountMfaRemove() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation<void, Error, void>({
|
||||
mutationFn: () => api.delete('/account/mfa/totp'),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['account', 'mfa'] }),
|
||||
});
|
||||
}
|
||||
|
||||
// --- Passkeys ---
|
||||
|
||||
export function useAccountPasskeyList() {
|
||||
return useQuery<PasskeyCredential[]>({
|
||||
queryKey: ['account', 'mfa', 'webauthn'],
|
||||
queryFn: () => api.get('/account/mfa/webauthn'),
|
||||
});
|
||||
}
|
||||
|
||||
export function useAccountRenamePasskey() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation<void, Error, { id: string; name: string }>({
|
||||
mutationFn: ({ id, name }) => api.patch(`/account/mfa/webauthn/${id}/name`, { name }),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['account', 'mfa'] }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useAccountDeletePasskey() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation<void, Error, string>({
|
||||
mutationFn: (id) => api.delete(`/account/mfa/webauthn/${id}`),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['account', 'mfa'] }),
|
||||
});
|
||||
}
|
||||
|
||||
// --- MFA Preference ---
|
||||
|
||||
export function useAccountMfaPreference() {
|
||||
return useMutation<void, Error, string>({
|
||||
mutationFn: (preference) => api.post('/account/mfa/method-preference', { preference }),
|
||||
});
|
||||
}
|
||||
43
ui/src/api/vendor-admin-hooks.ts
Normal file
43
ui/src/api/vendor-admin-hooks.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { api } from './client';
|
||||
import type { VendorAdmin, CreateAdminRequest, CreateAdminResponse } from '../types/api';
|
||||
|
||||
export function useVendorAdminList() {
|
||||
return useQuery<VendorAdmin[]>({
|
||||
queryKey: ['vendor', 'admins'],
|
||||
queryFn: () => api.get('/vendor/admins'),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateVendorAdmin() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation<CreateAdminResponse, Error, CreateAdminRequest>({
|
||||
mutationFn: (req) => api.post('/vendor/admins', req),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['vendor', 'admins'] }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useRemoveVendorAdmin() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation<void, Error, string>({
|
||||
mutationFn: (userId) => api.delete(`/vendor/admins/${userId}`),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['vendor', 'admins'] }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useResetVendorAdminPassword() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation<void, Error, { userId: string; password: string }>({
|
||||
mutationFn: ({ userId, password }) =>
|
||||
api.post(`/vendor/admins/${userId}/reset-password`, { password }),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['vendor', 'admins'] }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useResetVendorAdminMfa() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation<void, Error, string>({
|
||||
mutationFn: (userId) => api.delete(`/vendor/admins/${userId}/mfa`),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['vendor', 'admins'] }),
|
||||
});
|
||||
}
|
||||
@@ -273,3 +273,27 @@ export interface AuthPolicy {
|
||||
passkeyEnabled: boolean;
|
||||
passkeyMode: string;
|
||||
}
|
||||
|
||||
// Account profile types
|
||||
export interface AccountProfile {
|
||||
userId: string;
|
||||
name: string;
|
||||
email: string;
|
||||
}
|
||||
|
||||
// Vendor admin types
|
||||
export interface VendorAdmin {
|
||||
userId: string;
|
||||
name: string;
|
||||
email: string;
|
||||
}
|
||||
|
||||
export interface CreateAdminRequest {
|
||||
email: string;
|
||||
tempPassword?: string;
|
||||
}
|
||||
|
||||
export interface CreateAdminResponse {
|
||||
invited: boolean;
|
||||
tempPassword: string | null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user