FE_Dev / hooks /use-progress-manager.tsx
GitHub Actions
Deploy from GitHub Actions [dev] - 2025-10-31 07:28:50
68f7925
import type { ProcessingStep } from '@/types/processing';
import { useCallback, useEffect, useState } from 'react';
const DEFAULT_STEPS: ProcessingStep[] = [
{
id: 'preprocessing',
title: '็”ปๅƒๅ‰ๅ‡ฆ็†',
description: '็”ปๅƒใฎๅ‰ๅ‡ฆ็†ใ‚’ๅฎŸ่กŒใ—ใฆใ„ใพใ™',
status: 'pending',
estimatedTime: 10,
},
{
id: 'segmentation',
title: '่ฆ็ด ่ญ˜ๅˆฅ',
description: 'AIๅˆ†ๆžใซใ‚ˆใ‚Š็”ปๅƒใฎๅ„่ฆ็ด ใ‚’่ญ˜ๅˆฅใ—ใฆใ„ใพใ™',
status: 'pending',
estimatedTime: 30,
},
{
id: 'mask-generation',
title: 'ใƒžใ‚นใ‚ฏ็”Ÿๆˆ',
description: '็ทจ้›†็”จใฎใƒžใ‚นใ‚ฏใ‚’็”Ÿๆˆใ—ใฆใ„ใพใ™',
status: 'pending',
estimatedTime: 20,
},
{
id: 'background',
title: '่ƒŒๆ™ฏ่ฃœๅฎŒ',
description: 'ไธ่ฆใช่ฆ็ด ใ‚’้™คๅŽปใ—ใ€่ƒŒๆ™ฏใ‚’่‡ช็„ถใซ่ฃœๅฎŒใ—ใฆใ„ใพใ™',
status: 'pending',
estimatedTime: 45,
},
{
id: 'object',
title: 'ใ‚ชใƒ–ใ‚ธใ‚งใ‚ฏใƒˆๅ†็”Ÿๆˆ',
description: 'ไบบ็‰ฉใ‚„ๅ•†ๅ“ใ‚’่‘—ไฝœๆจฉใƒ•ใƒชใƒผใฎ็”ปๅƒใซ็ฝฎใๆ›ใˆใฆใ„ใพใ™',
status: 'pending',
estimatedTime: 60,
},
{
id: 'maincopy',
title: 'ใƒกใ‚คใƒณใ‚ณใƒ”ใƒผๅค‰ๆ›ด',
description: 'ๆ–ฐใ—ใ„ใƒกใ‚คใƒณใ‚ณใƒ”ใƒผใƒ†ใ‚ญใ‚นใƒˆใ‚’ๆ็”ปใ—ใฆใ„ใพใ™',
status: 'pending',
estimatedTime: 40,
},
{
id: 'header',
title: 'ใƒ˜ใƒƒใƒ€ใƒผๅˆๆˆ',
description: 'ๅ…ƒใฎใƒ˜ใƒƒใƒ€ใƒผใ‚’ไฟ่ญทใ—ใชใŒใ‚‰ๆœ€็ต‚็”ปๅƒใ‚’ๅˆๆˆใ—ใฆใ„ใพใ™',
status: 'pending',
estimatedTime: 20,
},
];
interface UseProgressManagerOptions {
initialSteps?: ProcessingStep[];
autoProgress?: boolean;
progressInterval?: number;
}
export function useProgressManager({ initialSteps = DEFAULT_STEPS, autoProgress = false, progressInterval = 1000 }: UseProgressManagerOptions = {}) {
const [steps, setSteps] = useState<ProcessingStep[]>(initialSteps);
const [currentStepId, setCurrentStepId] = useState<string | undefined>();
const [totalProgress, setTotalProgress] = useState(0);
const [estimatedTimeRemaining, setEstimatedTimeRemaining] = useState<number | undefined>();
const [isProcessing, setIsProcessing] = useState(false);
const [startTime, setStartTime] = useState<number | undefined>();
// ใ‚นใƒ†ใƒƒใƒ—ใ‚’้–‹ๅง‹ใ™ใ‚‹
const startStep = useCallback(
(stepId: string) => {
setSteps((prev) => prev.map((step) => (step.id === stepId ? { ...step, status: 'in_progress' as const } : step)));
setCurrentStepId(stepId);
if (!startTime) {
setStartTime(Date.now());
}
},
[startTime],
);
// ใ‚นใƒ†ใƒƒใƒ—ใ‚’ๅฎŒไบ†ใ™ใ‚‹
const completeStep = useCallback(
(stepId: string, actualTime?: number) => {
setSteps((prev) =>
prev.map((step) =>
step.id === stepId
? {
...step,
status: 'completed' as const,
actualTime: actualTime || step.estimatedTime,
}
: step,
),
);
// ๆฌกใฎใ‚นใƒ†ใƒƒใƒ—ใ‚’่‡ชๅ‹•็š„ใซ้–‹ๅง‹
const currentIndex = steps.findIndex((step) => step.id === stepId);
if (currentIndex >= 0 && currentIndex < steps.length - 1) {
const nextStep = steps[currentIndex + 1];
if (autoProgress) {
setTimeout(() => startStep(nextStep.id), 500);
}
} else {
// ๆœ€ๅพŒใฎใ‚นใƒ†ใƒƒใƒ—ใŒๅฎŒไบ†
setCurrentStepId(undefined);
setIsProcessing(false);
}
},
[steps, autoProgress, startStep],
);
// ใ‚นใƒ†ใƒƒใƒ—ใ‚’ใ‚จใƒฉใƒผใซใ™ใ‚‹
const errorStep = useCallback((stepId: string, errorMessage?: string) => {
setSteps((prev) =>
prev.map((step) =>
step.id === stepId
? {
...step,
status: 'error' as const,
description: errorMessage || step.description,
}
: step,
),
);
}, []);
// ๅ…จไฝ“ใฎ้€ฒ่กŒ็Šถๆณใ‚’่จˆ็ฎ—
useEffect(() => {
const completedSteps = steps.filter((step) => step.status === 'completed').length;
const currentStepIndex = currentStepId ? steps.findIndex((step) => step.id === currentStepId) : -1;
let progress = (completedSteps / steps.length) * 100;
// ็พๅœจๅฎŸ่กŒไธญใฎใ‚นใƒ†ใƒƒใƒ—ใฎ้€ฒ่กŒ็Šถๆณใ‚’ๆŽจๅฎš
if (currentStepIndex >= 0) {
const partialProgress = (1 / steps.length) * 0.5; // ็พๅœจใฎใ‚นใƒ†ใƒƒใƒ—ใฎ50%ใจใ—ใฆไปฎๅฎš
progress += partialProgress * 100;
}
setTotalProgress(Math.min(progress, 100));
}, [steps, currentStepId]);
// ๆฎ‹ใ‚Šๆ™‚้–“ใ‚’่จˆ็ฎ—
useEffect(() => {
if (!isProcessing || !currentStepId) {
setEstimatedTimeRemaining(undefined);
return;
}
const completedSteps = steps.filter((step) => step.status === 'completed');
const remainingSteps = steps.filter((step) => step.status === 'pending' || (step.status === 'in_progress' && step.id !== currentStepId));
const currentStep = steps.find((step) => step.id === currentStepId);
let remainingTime = 0;
// ๆฎ‹ใ‚Šใฎใ‚นใƒ†ใƒƒใƒ—ใฎๆŽจๅฎšๆ™‚้–“ใ‚’ๅˆ่จˆ
remainingSteps.forEach((step) => {
remainingTime += step.estimatedTime || 30;
});
// ็พๅœจใฎใ‚นใƒ†ใƒƒใƒ—ใฎๆฎ‹ใ‚Šๆ™‚้–“๏ผˆไปฎใซ50%ๅฎŒไบ†ใจไปฎๅฎš๏ผ‰
if (currentStep) {
remainingTime += (currentStep.estimatedTime || 30) * 0.5;
}
setEstimatedTimeRemaining(Math.ceil(remainingTime));
}, [steps, currentStepId, isProcessing]);
// ๅ‡ฆ็†ใ‚’้–‹ๅง‹ใ™ใ‚‹
const startProcessing = useCallback(() => {
setIsProcessing(true);
setStartTime(Date.now());
if (steps.length > 0) {
startStep(steps[0].id);
}
}, [steps, startStep]);
// ๅ‡ฆ็†ใ‚’ใƒชใ‚ปใƒƒใƒˆใ™ใ‚‹
const resetProgress = useCallback(() => {
setSteps(initialSteps);
setCurrentStepId(undefined);
setTotalProgress(0);
setEstimatedTimeRemaining(undefined);
setIsProcessing(false);
setStartTime(undefined);
}, [initialSteps]);
// ใ‚ซใ‚นใ‚ฟใƒ ใ‚นใƒ†ใƒƒใƒ—ใ‚’่จญๅฎšใ™ใ‚‹
const setCustomSteps = useCallback((newSteps: ProcessingStep[]) => {
setSteps(newSteps);
setCurrentStepId(undefined);
setTotalProgress(0);
}, []);
return {
steps,
currentStepId,
totalProgress,
estimatedTimeRemaining,
isProcessing,
startStep,
completeStep,
errorStep,
startProcessing,
resetProgress,
setCustomSteps,
};
}