feat: implement Phase 2 - Process Groups Management
All checks were successful
Build and Push Docker Image to Gitea / build-and-push (push) Successful in 58s

Features added:
- Group-based process organization with collapsible cards
- Batch operations for groups (Start All, Stop All, Restart All)
- Group statistics display (running, stopped, fatal counts)
- Dedicated /groups page for group-centric management
- View toggle in /processes page (Flat view | Grouped view)

Implementation details:
- Created group API routes: /api/supervisor/groups/[name]/{start,stop,restart}
- Added React Query hooks: useStartProcessGroup, useStopProcessGroup, useRestartProcessGroup
- Created components: GroupCard, GroupView, GroupSelector
- Updated Navbar with Groups navigation link
- Integrated grouped view in processes page with toggle

Phase 2 complete (6-8 hours estimated)
This commit is contained in:
2025-11-23 19:08:10 +01:00
parent f2d89d8333
commit 5c028cdc11
11 changed files with 1365 additions and 4 deletions

View File

@@ -284,3 +284,89 @@ export function useClearAllLogs() {
},
});
}
// Process Group Management
async function startProcessGroup(name: string, wait: boolean = true): Promise<{ success: boolean; message: string; results: any[] }> {
const response = await fetch(`/api/supervisor/groups/${encodeURIComponent(name)}/start`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ wait }),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || 'Failed to start process group');
}
return response.json();
}
async function stopProcessGroup(name: string, wait: boolean = true): Promise<{ success: boolean; message: string; results: any[] }> {
const response = await fetch(`/api/supervisor/groups/${encodeURIComponent(name)}/stop`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ wait }),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || 'Failed to stop process group');
}
return response.json();
}
async function restartProcessGroup(name: string, wait: boolean = true): Promise<{ success: boolean; message: string; results: any[] }> {
const response = await fetch(`/api/supervisor/groups/${encodeURIComponent(name)}/restart`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ wait }),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || 'Failed to restart process group');
}
return response.json();
}
export function useStartProcessGroup() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ name, wait }: { name: string; wait?: boolean }) => startProcessGroup(name, wait),
onSuccess: (data) => {
toast.success(data.message);
queryClient.invalidateQueries({ queryKey: supervisorKeys.processes() });
},
onError: (error: Error) => {
toast.error(`Failed to start process group: ${error.message}`);
},
});
}
export function useStopProcessGroup() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ name, wait }: { name: string; wait?: boolean }) => stopProcessGroup(name, wait),
onSuccess: (data) => {
toast.success(data.message);
queryClient.invalidateQueries({ queryKey: supervisorKeys.processes() });
},
onError: (error: Error) => {
toast.error(`Failed to stop process group: ${error.message}`);
},
});
}
export function useRestartProcessGroup() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ name, wait }: { name: string; wait?: boolean }) => restartProcessGroup(name, wait),
onSuccess: (data) => {
toast.success(data.message);
queryClient.invalidateQueries({ queryKey: supervisorKeys.processes() });
},
onError: (error: Error) => {
toast.error(`Failed to restart process group: ${error.message}`);
},
});
}