Merge branch 'fe/refactor/project-create-modal' into 'fe/develop'
Refactor: 파일로 카테고리 추가하는 기능 구현(cvat 모방) See merge request s11-s-project/S11P21S002!270
This commit is contained in:
commit
29bc04bced
@ -7,6 +7,8 @@ import { Button } from '../ui/button';
|
|||||||
import { RadioGroup, RadioGroupItem } from '../ui/radio-group';
|
import { RadioGroup, RadioGroupItem } from '../ui/radio-group';
|
||||||
import { useRef, useState } from 'react';
|
import { useRef, useState } from 'react';
|
||||||
import { X } from 'lucide-react';
|
import { X } from 'lucide-react';
|
||||||
|
import { Tabs, TabsList, TabsTrigger, TabsContent } from '../ui/tabs';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
projectName: z.string().max(50).min(1, {
|
projectName: z.string().max(50).min(1, {
|
||||||
@ -27,11 +29,14 @@ const defaultValues: Partial<ProjectCreateFormValues> = {
|
|||||||
|
|
||||||
export default function ProjectCreateForm({ onSubmit }: { onSubmit: (data: ProjectCreateFormValues) => void }) {
|
export default function ProjectCreateForm({ onSubmit }: { onSubmit: (data: ProjectCreateFormValues) => void }) {
|
||||||
const [categories, setCategories] = useState<string[]>([]);
|
const [categories, setCategories] = useState<string[]>([]);
|
||||||
|
const [isDragging, setIsDragging] = useState<boolean>(false);
|
||||||
const form = useForm<ProjectCreateFormValues>({
|
const form = useForm<ProjectCreateFormValues>({
|
||||||
resolver: zodResolver(formSchema),
|
resolver: zodResolver(formSchema),
|
||||||
defaultValues: { ...defaultValues, categories },
|
defaultValues: { ...defaultValues, categories },
|
||||||
});
|
});
|
||||||
const categoryRef = useRef<HTMLInputElement>(null);
|
const categoryRef = useRef<HTMLInputElement>(null);
|
||||||
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
const handleAddCategory = (event: React.MouseEvent<HTMLButtonElement>, onChange: (value: string[]) => void) => {
|
const handleAddCategory = (event: React.MouseEvent<HTMLButtonElement>, onChange: (value: string[]) => void) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
@ -47,6 +52,63 @@ export default function ProjectCreateForm({ onSubmit }: { onSubmit: (data: Proje
|
|||||||
categoryRef.current!.value = '';
|
categoryRef.current!.value = '';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleFileUpload = async (event: React.ChangeEvent<HTMLInputElement>, onChange: (value: string[]) => void) => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const file = event.target.files?.[0];
|
||||||
|
if (!file) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const text = await file.text();
|
||||||
|
const newCategories = text
|
||||||
|
.split('\n')
|
||||||
|
.map((line) => line.trim())
|
||||||
|
.filter((line) => line.length > 0);
|
||||||
|
|
||||||
|
if (newCategories.length > 0) {
|
||||||
|
const uniqueCategories = Array.from(new Set([...categories, ...newCategories]));
|
||||||
|
onChange(uniqueCategories);
|
||||||
|
setCategories(uniqueCategories);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('파일을 읽는 중 오류가 발생했습니다:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDragOver = (event: React.DragEvent<HTMLDivElement>) => {
|
||||||
|
event.preventDefault();
|
||||||
|
setIsDragging(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDragLeave = (event: React.DragEvent<HTMLDivElement>) => {
|
||||||
|
event.preventDefault();
|
||||||
|
setIsDragging(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDrop = async (event: React.DragEvent<HTMLDivElement>, onChange: (value: string[]) => void) => {
|
||||||
|
event.preventDefault();
|
||||||
|
setIsDragging(false);
|
||||||
|
|
||||||
|
const file = event.dataTransfer.files?.[0];
|
||||||
|
if (!file) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const text = await file.text();
|
||||||
|
const newCategories = text
|
||||||
|
.split('\n')
|
||||||
|
.map((line) => line.trim())
|
||||||
|
.filter((line) => line.length > 0);
|
||||||
|
|
||||||
|
if (newCategories.length > 0) {
|
||||||
|
const uniqueCategories = Array.from(new Set([...categories, ...newCategories]));
|
||||||
|
onChange(uniqueCategories);
|
||||||
|
setCategories(uniqueCategories);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('파일을 읽는 중 오류가 발생했습니다:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
@ -106,20 +168,51 @@ export default function ProjectCreateForm({ onSubmit }: { onSubmit: (data: Proje
|
|||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<div className="mb-1 flex w-full flex-col gap-2">
|
<div className="mb-1 flex w-full flex-col gap-2">
|
||||||
<div className="body-strong">카테고리</div>
|
<div className="body-strong">카테고리</div>
|
||||||
<div className="flex gap-2">
|
<Tabs defaultValue="manual">
|
||||||
<FormControl>
|
<TabsList className="mb-4">
|
||||||
<Input
|
<TabsTrigger value="manual">직접 입력</TabsTrigger>
|
||||||
ref={categoryRef}
|
<TabsTrigger value="file">파일로 입력</TabsTrigger>
|
||||||
placeholder="카테고리를 추가해주세요."
|
</TabsList>
|
||||||
/>
|
<TabsContent value="manual">
|
||||||
</FormControl>
|
<div className="mt-2 flex gap-2">
|
||||||
<Button
|
<FormControl>
|
||||||
variant="blue"
|
<Input
|
||||||
onClick={(event) => handleAddCategory(event, field.onChange)}
|
ref={categoryRef}
|
||||||
>
|
placeholder="카테고리를 추가해주세요."
|
||||||
추가
|
/>
|
||||||
</Button>
|
</FormControl>
|
||||||
</div>
|
<Button
|
||||||
|
variant="blue"
|
||||||
|
onClick={(event) => handleAddCategory(event, field.onChange)}
|
||||||
|
>
|
||||||
|
추가
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</TabsContent>
|
||||||
|
<TabsContent value="file">
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
'mt-2 flex w-full items-center justify-center rounded-lg border-2 border-dashed p-5 text-center transition-colors',
|
||||||
|
isDragging ? 'border-blue-500 bg-blue-100' : 'border-gray-300 bg-gray-100'
|
||||||
|
)}
|
||||||
|
onDragOver={handleDragOver}
|
||||||
|
onDragLeave={handleDragLeave}
|
||||||
|
onDrop={(event) => handleDrop(event, field.onChange)}
|
||||||
|
onClick={() => fileInputRef.current?.click()}
|
||||||
|
>
|
||||||
|
<p className="text-gray-500">
|
||||||
|
파일을 업로드하려면 여기를 클릭하거나 파일을 드래그하여 여기에 놓으세요.
|
||||||
|
</p>
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
ref={fileInputRef}
|
||||||
|
accept=".txt"
|
||||||
|
onChange={(event) => handleFileUpload(event, field.onChange)}
|
||||||
|
className="hidden"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</TabsContent>
|
||||||
|
</Tabs>
|
||||||
{categories.length > 0 && (
|
{categories.length > 0 && (
|
||||||
<div className="flex w-full flex-col overflow-x-auto">
|
<div className="flex w-full flex-col overflow-x-auto">
|
||||||
<div className="flex gap-2 py-1">
|
<div className="flex gap-2 py-1">
|
||||||
|
Loading…
Reference in New Issue
Block a user