Merge branch 'fe/refactor/learn' into 'fe/develop'

Fe/refactor/learn

See merge request s11-s-project/S11P21S002!255
This commit is contained in:
조현수 2024-10-01 14:10:40 +09:00
commit 1a64e28b69
11 changed files with 180 additions and 40 deletions

View File

@ -19,6 +19,7 @@
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-tabs": "^1.1.0",
"@radix-ui/react-toast": "^1.2.1",
"@radix-ui/react-toggle": "^1.1.0",
"@radix-ui/react-tooltip": "^1.1.2",
"@tanstack/react-query": "^5.52.1",
"axios": "^1.7.5",
@ -4654,6 +4655,30 @@
}
}
},
"node_modules/@radix-ui/react-toggle": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.1.0.tgz",
"integrity": "sha512-gwoxaKZ0oJ4vIgzsfESBuSgJNdc0rv12VhHgcqN0TEJmmZixXG/2XpsLK8kzNWYcnaoRIEEQc0bEi3dIvdUpjw==",
"dependencies": {
"@radix-ui/primitive": "1.1.0",
"@radix-ui/react-primitive": "2.0.0",
"@radix-ui/react-use-controllable-state": "1.1.0"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-tooltip": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.1.2.tgz",

View File

@ -25,6 +25,7 @@
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-tabs": "^1.1.0",
"@radix-ui/react-toast": "^1.2.1",
"@radix-ui/react-toggle": "^1.1.0",
"@radix-ui/react-tooltip": "^1.1.2",
"@tanstack/react-query": "^5.52.1",
"axios": "^1.7.5",

View File

@ -3,7 +3,7 @@ import { Image, Layer, Stage, Line, Rect, Text, Group } from 'react-konva';
import useImage from 'use-image';
import { Label, Shape } from '@/types';
import useCommentListQuery from '@/queries/comments/useCommentListQuery';
import { Button } from '@/components/ui/button';
import { Toggle } from '@/components/ui/toggle';
interface ImageWithLabelsProps {
imagePath: string;
@ -63,14 +63,15 @@ export default function ImageWithLabels({ imagePath, labelData, projectId, image
return (
<div>
<Button
<Toggle
variant="outline"
size="sm"
onClick={() => setShowComments((prev) => !prev)}
pressed={showComments}
onPressedChange={(pressed) => setShowComments(pressed)}
className="mb-4"
>
{showComments ? '댓글 숨기기' : '댓글 보기'}
</Button>
</Toggle>
<Stage
width={stageDimensions.width}
height={stageDimensions.height}

View File

@ -1,6 +1,6 @@
import { useEffect } from 'react';
import ModelLineChart from './ModelLineChart';
import usePollingTrainingModelReport from '@/queries/reports/usePollingModelReportsQuery';
import usePollingTrainingModelReport from '@/hooks/usePollingTrainingModelReport';
import { useQueryClient } from '@tanstack/react-query';
import { ModelResponse } from '@/types';
@ -13,16 +13,27 @@ interface TrainingGraphProps {
export default function TrainingGraph({ projectId, selectedModel, className }: TrainingGraphProps) {
const queryClient = useQueryClient();
const { data: trainingDataList } = usePollingTrainingModelReport(
projectId as number,
selectedModel?.id as number,
selectedModel?.isTrain || false
);
const isTrainingEnabled = Boolean(selectedModel?.isTrain);
const handleTrainingEnd = () => {
queryClient.resetQueries({
queryKey: ['modelReports', projectId, selectedModel?.id],
exact: true,
});
alert('학습이 완료되었습니다.');
};
const { data: trainingDataList } = usePollingTrainingModelReport({
projectId: projectId as number,
modelId: selectedModel?.id as number,
enabled: isTrainingEnabled,
onTrainingEnd: handleTrainingEnd,
});
useEffect(() => {
if (!selectedModel || !selectedModel.isTrain) {
queryClient.resetQueries({
queryKey: [{ type: 'modelReports', projectId, modelId: selectedModel?.id }],
queryKey: ['modelReports', projectId, selectedModel?.id],
exact: true,
});
}

View File

@ -2,6 +2,7 @@ import { useState, useEffect } from 'react';
import TrainingSettings from './TrainingSettings';
import TrainingGraph from './TrainingGraph';
import useTrainModelQuery from '@/queries/models/useTrainModelQuery';
import usePollingTrainingModelReport from '@/hooks/usePollingTrainingModelReport';
import { ModelTrainRequest, ModelResponse } from '@/types';
import { useQueryClient } from '@tanstack/react-query';
@ -24,6 +25,11 @@ export default function TrainingTab({ projectId }: TrainingTabProps) {
}
};
const handleTrainingEnd = () => {
setIsPolling(false);
setSelectedModel((prevModel) => (prevModel ? { ...prevModel, isTrain: false } : null));
};
useEffect(() => {
if (!selectedModel || !numericProjectId || !isPolling) return;
@ -38,10 +44,10 @@ export default function TrainingTab({ projectId }: TrainingTabProps) {
setSelectedModel(updatedModel);
if (updatedModel.isTrain) {
setIsPolling(true);
} else if (!updatedModel.isTrain && selectedModel.isTrain) {
setIsPolling(false);
setSelectedModel(null);
} else {
setIsPolling(false);
setSelectedModel({ ...updatedModel, isTrain: false });
}
}
}, 2000);
@ -51,14 +57,22 @@ export default function TrainingTab({ projectId }: TrainingTabProps) {
};
}, [selectedModel, numericProjectId, queryClient, isPolling]);
usePollingTrainingModelReport({
projectId: numericProjectId as number,
modelId: selectedModel?.id as number,
enabled: selectedModel?.isTrain || false,
onTrainingEnd: handleTrainingEnd,
});
const handleTrainingStop = () => {
setIsPolling(false);
setSelectedModel((prevModel) => (prevModel ? { ...prevModel, isTrain: false } : null));
//todo: 중단 함수 연결
};
return (
<div className="grid grid-rows-[auto_1fr] gap-8 md:grid-cols-2">
<TrainingSettings
key={`${selectedModel?.isTrain ? 'training' : 'settings'}-${isPolling}`}
projectId={numericProjectId}
selectedModel={selectedModel}
setSelectedModel={setSelectedModel}
@ -68,7 +82,6 @@ export default function TrainingTab({ projectId }: TrainingTabProps) {
className="h-full"
/>
<TrainingGraph
key={`${selectedModel?.isTrain ? 'training' : 'graph'}-${isPolling}`}
projectId={numericProjectId}
selectedModel={selectedModel}
className="h-full"

View File

@ -13,8 +13,8 @@ const buttonVariants = cva(
// 'bg-gray-900 text-gray-50 hover:bg-gray-900/90 dark:bg-gray-50 dark:text-gray-900 dark:hover:bg-gray-50/90',
// destructive:
// 'bg-red-500 text-gray-50 hover:bg-red-500/90 dark:bg-red-900 dark:text-gray-50 dark:hover:bg-red-900/90',
outline:
'border border-gray-200 bg-white hover:bg-gray-100 hover:text-gray-900 dark:border-gray-800 dark:bg-gray-950 dark:hover:bg-gray-800 dark:hover:text-gray-50',
// outline:
// 'border border-gray-200 bg-white hover:bg-gray-100 hover:text-gray-900 dark:border-gray-800 dark:bg-gray-950 dark:hover:bg-gray-800 dark:hover:text-gray-50',
// outlinePrimary:
// 'border border-primary text-primary hover:bg-primary hover:text-white dark:border-primary dark:text-primary dark:hover:bg-primary dark:hover:text-white disabled:border-gray-200 disabled:bg-white disabled:text-gray-500 disabled:hover:bg-gray-100 disabled:hover:text-gray-300',
// secondary:

View File

@ -0,0 +1,44 @@
'use client';
import * as React from 'react';
import * as TogglePrimitive from '@radix-ui/react-toggle';
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@/lib/utils';
const toggleVariants = cva(
'inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground',
{
variants: {
variant: {
default: 'bg-transparent',
outline: 'border border-input bg-transparent hover:bg-accent hover:text-accent-foreground',
},
size: {
default: 'h-10 px-3',
sm: 'h-9 px-2.5',
lg: 'h-11 px-5',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
}
);
const Toggle = React.forwardRef<
React.ElementRef<typeof TogglePrimitive.Root>,
React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root> & VariantProps<typeof toggleVariants>
>(({ className, variant, size, ...props }, ref) => (
<TogglePrimitive.Root
ref={ref}
className={cn(toggleVariants({ variant, size, className }))}
{...props}
/>
));
Toggle.displayName = TogglePrimitive.Root.displayName;
// eslint-disable-next-line react-refresh/only-export-components
export { Toggle, toggleVariants };

View File

@ -0,0 +1,43 @@
import { useEffect } from 'react';
import { useQuery } from '@tanstack/react-query';
import { getTrainingModelReport } from '@/api/reportApi';
import { ReportResponse } from '@/types';
interface UsePollingTrainingModelReportProps {
projectId: number;
modelId: number;
enabled: boolean;
onTrainingEnd: () => void;
}
export default function usePollingTrainingModelReport({
projectId,
modelId,
enabled,
onTrainingEnd,
}: UsePollingTrainingModelReportProps) {
const query = useQuery<ReportResponse[]>({
queryKey: ['modelReports', projectId, modelId],
queryFn: () => getTrainingModelReport(projectId, modelId),
enabled,
refetchInterval: (data) => {
if (!enabled) return false;
if (Array.isArray(data)) {
const lastReport = data[data.length - 1];
return lastReport?.epochTime ? Math.max(2000, lastReport.epochTime / 2) : 2000;
}
return 2000;
},
});
useEffect(() => {
if (query.data && query.data.length > 0) {
const lastReport = query.data[query.data.length - 1];
if (lastReport.epoch >= lastReport.totalEpochs) {
onTrainingEnd();
}
}
}, [query.data, onTrainingEnd]);
return query;
}

View File

@ -1,12 +0,0 @@
import { useQuery } from '@tanstack/react-query';
import { getTrainingModelReport } from '@/api/reportApi';
import { ReportResponse } from '@/types';
export default function usePollingTrainingModelReport(projectId: number, modelId: number, enabled: boolean) {
return useQuery<ReportResponse[]>({
queryKey: ['modelReports', projectId, modelId],
queryFn: () => getTrainingModelReport(projectId, modelId),
refetchInterval: enabled ? 5000 : false,
enabled,
});
}

View File

@ -1,10 +0,0 @@
import { useSuspenseQuery } from '@tanstack/react-query';
import { getTrainingModelReport } from '@/api/reportApi';
import { ReportResponse } from '@/types';
export default function useTrainingModelReport(projectId: number, modelId: number) {
return useSuspenseQuery<ReportResponse[]>({
queryKey: ['modelReports', projectId, modelId],
queryFn: () => getTrainingModelReport(projectId, modelId),
});
}

View File

@ -0,0 +1,24 @@
import { useQuery } from '@tanstack/react-query';
import { getTrainingModelReport } from '@/api/reportApi';
import { ReportResponse } from '@/types';
interface UseTrainingModelReportQueryProps {
projectId: number;
modelId: number;
enabled?: boolean;
refetchInterval?: number | false;
}
export default function useTrainingModelReportQuery({
projectId,
modelId,
enabled = true,
refetchInterval = false,
}: UseTrainingModelReportQueryProps) {
return useQuery<ReportResponse[]>({
queryKey: ['modelReports', projectId, modelId],
queryFn: () => getTrainingModelReport(projectId, modelId),
enabled,
refetchInterval,
});
}