From fe0a5d079bf42d872c75b4b70e73aa1e07996fe4 Mon Sep 17 00:00:00 2001 From: seaznCode Date: Tue, 20 Jan 2026 20:49:11 +0100 Subject: [PATCH] feat: enhance dev management page with status updates for structure, loose files, and ghost directories --- .../hooks/exoscaleMaintenance.ts | 2 + src/app/admin/dev-management/page.tsx | 124 +++++++++++++----- 2 files changed, 96 insertions(+), 30 deletions(-) diff --git a/src/app/admin/dev-management/hooks/exoscaleMaintenance.ts b/src/app/admin/dev-management/hooks/exoscaleMaintenance.ts index 2e739c4..4f25d6b 100644 --- a/src/app/admin/dev-management/hooks/exoscaleMaintenance.ts +++ b/src/app/admin/dev-management/hooks/exoscaleMaintenance.ts @@ -32,6 +32,7 @@ export type GhostDirectory = { export type FixResult = { userId: number; + contractCategory?: string; created?: number; moved: number; skipped: number; @@ -40,6 +41,7 @@ export type FixResult = { export type FixMeta = { processedUsers?: number; + createdTotal?: number; movedTotal?: number; errorCount?: number; }; diff --git a/src/app/admin/dev-management/page.tsx b/src/app/admin/dev-management/page.tsx index 7457a35..070de21 100644 --- a/src/app/admin/dev-management/page.tsx +++ b/src/app/admin/dev-management/page.tsx @@ -63,6 +63,11 @@ export default function DevManagementPage() { const [structureActionResults, setStructureActionResults] = React.useState([]) const [looseActionMeta, setLooseActionMeta] = React.useState<{ processedUsers?: number; movedTotal?: number; errorCount?: number } | null>(null) const [looseActionResults, setLooseActionResults] = React.useState([]) + const [structureStatus, setStructureStatus] = React.useState('') + const [looseStatus, setLooseStatus] = React.useState('') + const [ghostStatus, setGhostStatus] = React.useState('') + + const formatNow = () => new Date().toLocaleString() const runImport = async () => { setError('') @@ -93,6 +98,7 @@ export default function DevManagementPage() { const loadStructureIssues = async () => { setExoscaleError('') setExoscaleLoading(true) + setStructureStatus('Refreshing list...') try { const res = await listFolderStructureIssues() if (!res.ok) { @@ -101,6 +107,7 @@ export default function DevManagementPage() { } setStructureUsers(res.data || []) setStructureMeta(res.meta || null) + setStructureStatus(`Last refresh: ${formatNow()}`) } finally { setExoscaleLoading(false) } @@ -109,6 +116,7 @@ export default function DevManagementPage() { const loadLooseFiles = async () => { setExoscaleError('') setExoscaleLoading(true) + setLooseStatus('Refreshing list...') try { const res = await listLooseFiles() if (!res.ok) { @@ -117,6 +125,7 @@ export default function DevManagementPage() { } setLooseUsers(res.data || []) setLooseMeta(res.meta || null) + setLooseStatus(`Last refresh: ${formatNow()}`) } finally { setExoscaleLoading(false) } @@ -125,6 +134,7 @@ export default function DevManagementPage() { const loadGhostDirectories = async () => { setExoscaleError('') setExoscaleLoading(true) + setGhostStatus('Refreshing list...') try { const res = await listGhostDirectories() if (!res.ok) { @@ -133,6 +143,7 @@ export default function DevManagementPage() { } setGhostDirs(res.data || []) setGhostMeta(res.meta || null) + setGhostStatus(`Last refresh: ${formatNow()}`) } finally { setExoscaleLoading(false) } @@ -140,24 +151,40 @@ export default function DevManagementPage() { const runCreateStructure = async (userId?: number) => { setExoscaleError('') - if (userId) setFixingUserId(userId) - else setFixingAll(true) + if (userId) { + setFixingUserId(userId) + setStructureStatus(`Creating folders for #${userId}...`) + } else { + setFixingAll(true) + setStructureStatus('Creating folders for each user...') + setStructureActionResults([]) + setStructureActionMeta({ processedUsers: 0, createdTotal: 0, errorCount: 0 }) + } try { - const res = await createFolderStructure(userId) - if (!res.ok) { - setExoscaleError(res.message || 'Failed to create folder structure.') - return - } - setStructureActionMeta(res.meta || null) - setStructureActionResults(res.data || []) - setFixResults(prev => { - const next = { ...prev } - for (const item of res.data || []) { - next[item.userId] = item + const targets = userId ? [{ userId }] : structureUsers.map(u => ({ userId: u.userId })) + for (const target of targets) { + const res = await createFolderStructure(target.userId) + if (!res.ok) { + setExoscaleError(res.message || 'Failed to create folder structure.') + break } - return next - }) + const batch = res.data || [] + setStructureActionResults(prev => [...prev, ...batch]) + setStructureActionMeta(prev => ({ + processedUsers: (prev?.processedUsers || 0) + (res.meta?.processedUsers || 0), + createdTotal: (prev?.createdTotal || 0) + (res.meta?.createdTotal || 0), + errorCount: (prev?.errorCount || 0) + (res.meta?.errorCount || 0) + })) + setFixResults(prev => { + const next = { ...prev } + for (const item of batch) { + next[item.userId] = item + } + return next + }) + setStructureStatus(`Last create: ${formatNow()} (processed #${target.userId})`) + } await loadStructureIssues() } finally { setFixingUserId(null) @@ -167,24 +194,40 @@ export default function DevManagementPage() { const runMoveLooseFiles = async (userId?: number) => { setExoscaleError('') - if (userId) setFixingUserId(userId) - else setFixingAll(true) + if (userId) { + setFixingUserId(userId) + setLooseStatus(`Moving loose files for #${userId}...`) + } else { + setFixingAll(true) + setLooseStatus('Moving loose files for each user...') + setLooseActionResults([]) + setLooseActionMeta({ processedUsers: 0, movedTotal: 0, errorCount: 0 }) + } try { - const res = await moveLooseFilesToContract(userId) - if (!res.ok) { - setExoscaleError(res.message || 'Failed to move loose files.') - return - } - setLooseActionMeta(res.meta || null) - setLooseActionResults(res.data || []) - setFixResults(prev => { - const next = { ...prev } - for (const item of res.data || []) { - next[item.userId] = item + const targets = userId ? [{ userId }] : looseUsers.map(u => ({ userId: u.userId })) + for (const target of targets) { + const res = await moveLooseFilesToContract(target.userId) + if (!res.ok) { + setExoscaleError(res.message || 'Failed to move loose files.') + break } - return next - }) + const batch = res.data || [] + setLooseActionResults(prev => [...prev, ...batch]) + setLooseActionMeta(prev => ({ + processedUsers: (prev?.processedUsers || 0) + (res.meta?.processedUsers || 0), + movedTotal: (prev?.movedTotal || 0) + (res.meta?.movedTotal || 0), + errorCount: (prev?.errorCount || 0) + (res.meta?.errorCount || 0) + })) + setFixResults(prev => { + const next = { ...prev } + for (const item of batch) { + next[item.userId] = item + } + return next + }) + setLooseStatus(`Last move: ${formatNow()} (processed #${target.userId})`) + } await loadLooseFiles() } finally { setFixingUserId(null) @@ -192,6 +235,12 @@ export default function DevManagementPage() { } } + React.useEffect(() => { + if (activeTab === 'structure') loadStructureIssues() + if (activeTab === 'loose') loadLooseFiles() + if (activeTab === 'ghost') loadGhostDirectories() + }, [activeTab]) + const onImportFile = (e: React.ChangeEvent) => { const file = e.target.files?.[0] if (!file) return @@ -374,6 +423,9 @@ export default function DevManagementPage() {

Ensures both contract and gdpr folders exist for each user.

+ {structureStatus && ( +
{structureStatus}
+ )}