feat: standardize input styling with tier system, add depth and admin features
- Define 4 input tier CSS classes (input-standard, input-search, input-embedded, input-danger) in globals.css, migrating ~50 inputs across ~28 component files to use them - Add subtle border and inset shadow to solid input tiers for resting-state visibility - Fix focus ring clipping in settings panel scroll container - Fix phantom Tailwind tokens (border-border-primary, placeholder-txt-muted) - Add admin user management panel, storage management, and account deletion utilities
This commit is contained in:
@@ -34,6 +34,12 @@ import type {
|
||||
ChangePasswordRequest,
|
||||
ChangePasswordResponse,
|
||||
DeleteAccountRequest,
|
||||
StorageStats,
|
||||
OrphanedFile,
|
||||
CleanupResult,
|
||||
AdminUserListResponse,
|
||||
AdminUser,
|
||||
AdminResetPasswordResponse,
|
||||
ExploreSpace,
|
||||
JoinRequest,
|
||||
Role,
|
||||
@@ -181,6 +187,16 @@ export class BackspaceApiClient {
|
||||
myJoinRequests: (status?: string) => Promise<{ requests: JoinRequest[] }>;
|
||||
};
|
||||
|
||||
readonly admin: {
|
||||
storageStats: () => Promise<StorageStats>;
|
||||
storageOrphans: () => Promise<{ orphans: OrphanedFile[] }>;
|
||||
storageCleanup: (dryRun?: boolean) => Promise<CleanupResult>;
|
||||
listUsers: (params?: { q?: string; page?: number; pageSize?: number; showDeleted?: boolean }) => Promise<AdminUserListResponse>;
|
||||
setUserRole: (userId: string, isAdmin: boolean) => Promise<AdminUser>;
|
||||
resetUserPassword: (userId: string) => Promise<AdminResetPasswordResponse>;
|
||||
deleteUser: (userId: string) => Promise<{ success: boolean }>;
|
||||
};
|
||||
|
||||
constructor(baseUrl: string, getToken: () => string | null) {
|
||||
async function request<T>(
|
||||
method: string,
|
||||
@@ -487,6 +503,26 @@ export class BackspaceApiClient {
|
||||
return request<{ requests: JoinRequest[] }>('GET', `/users/@me/join-requests?${params}`);
|
||||
},
|
||||
};
|
||||
|
||||
this.admin = {
|
||||
storageStats: () => request<StorageStats>('GET', '/admin/storage/stats'),
|
||||
storageOrphans: () => request<{ orphans: OrphanedFile[] }>('GET', '/admin/storage/orphans'),
|
||||
storageCleanup: (dryRun = false) => request<CleanupResult>('POST', '/admin/storage/cleanup', { dryRun }),
|
||||
listUsers: (params) => {
|
||||
const qs = new URLSearchParams();
|
||||
if (params?.q) qs.set('q', params.q);
|
||||
if (params?.page !== undefined) qs.set('page', String(params.page));
|
||||
if (params?.pageSize !== undefined) qs.set('pageSize', String(params.pageSize));
|
||||
if (params?.showDeleted) qs.set('showDeleted', 'true');
|
||||
return request<AdminUserListResponse>('GET', `/admin/users?${qs}`);
|
||||
},
|
||||
setUserRole: (userId, isAdmin) =>
|
||||
request<AdminUser>('PATCH', `/admin/users/${userId}/role`, { isAdmin }),
|
||||
resetUserPassword: (userId) =>
|
||||
request<AdminResetPasswordResponse>('POST', `/admin/users/${userId}/reset-password`),
|
||||
deleteUser: (userId) =>
|
||||
request<{ success: boolean }>('DELETE', `/admin/users/${userId}`),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user