From fe30cab457640c23b1e18338b86b0be894559631 Mon Sep 17 00:00:00 2001
From: Kim DoKyun <51306225+jasper200207@users.noreply.github.com>
Date: Thu, 16 May 2024 19:02:16 +0900
Subject: [PATCH] =?UTF-8?q?Feature/#187=20=ED=95=99=EC=8A=B5=EC=9E=90?=
 =?UTF-8?q?=EB=A3=8C=20=EC=83=9D=EC=84=B1=EB=AA=A8=EB=8B=AC=20(#212)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* feat : 제목, 소개, 공개범위

#187

* feat : 파일 삽입 기능 추가

#187

* design : 추가하기 버튼 그림자 추가

#187

* design : divider 위치 변경

#187

* design : minH : 40px 추가

#187
---
 src/components/IconBox/index.tsx             |   2 +-
 src/containers/study/DocumentModal/index.tsx | 165 +++++++++++++++++++
 src/containers/study/DocumentModal/type.ts   |  15 ++
 3 files changed, 181 insertions(+), 1 deletion(-)
 create mode 100644 src/containers/study/DocumentModal/index.tsx
 create mode 100644 src/containers/study/DocumentModal/type.ts

diff --git a/src/components/IconBox/index.tsx b/src/components/IconBox/index.tsx
index 12791e9e..279f742c 100644
--- a/src/components/IconBox/index.tsx
+++ b/src/components/IconBox/index.tsx
@@ -4,7 +4,7 @@ import { IconBoxProps } from './type';
 
 const IconBox = ({ leftIcon, content, rightIcon, handleClick }: IconBoxProps) => {
   return (
-    <Flex align="center" gap="1" w="100%" h="40px" color="white" bg="orange_light" borderRadius="2xl">
+    <Flex align="center" gap="1" w="100%" h="40px" minH="40px" color="white" bg="orange_light" borderRadius="2xl">
       <IconButton as="div" flexShrink="0" aria-label="" icon={leftIcon} size="icon_md" variant="transparent" />
       <Text textStyle="bold_md" flex="auto" cursor="default" isTruncated>
         {content}
diff --git a/src/containers/study/DocumentModal/index.tsx b/src/containers/study/DocumentModal/index.tsx
new file mode 100644
index 00000000..d5dacda2
--- /dev/null
+++ b/src/containers/study/DocumentModal/index.tsx
@@ -0,0 +1,165 @@
+'use client';
+
+import { Divider, Flex, Input, Text, Textarea, Button } from '@chakra-ui/react';
+import { ChangeEvent, useRef, useState } from 'react';
+import { BiFile, BiImage, BiTrash } from 'react-icons/bi';
+import { BsLink45Deg } from 'react-icons/bs';
+
+import IconBox from '@/components/IconBox';
+import ActionModal from '@/components/Modal/ActionModal';
+import StyledRadio from '@/components/StyledRadio';
+import StyledRadioGroup from '@/components/StyledRadioGroup';
+import color from '@/constants/color';
+import { DocumentModalProps, DocumentType, DocumentList } from '@/containers/study/DocumentModal/type';
+
+const DocumentBoxIcon = {
+  img: <BiImage />,
+  file: <BiFile />,
+  url: <BsLink45Deg />,
+};
+
+const DocumentModal = ({ isOpen, onClose }: DocumentModalProps) => {
+  const [doctype, setDocType] = useState<DocumentType>('img');
+  const [docList, setDocList] = useState<DocumentList>({
+    img: [],
+    file: [],
+    url: [],
+  });
+  const imgInputRef = useRef<HTMLInputElement>(null);
+  const fileInputRef = useRef<HTMLInputElement>(null);
+  const urlInputRef = useRef<HTMLInputElement>(null);
+
+  const onConfirmButtonClick = () => {
+    onClose();
+  };
+
+  const handleGetDoc = {
+    img: (e: ChangeEvent<HTMLInputElement>) => {
+      const imgs = Array.from(e.target.files || []);
+      setDocList((prev) => ({
+        ...prev,
+        img: [
+          ...prev.img,
+          ...imgs.map((img) => ({
+            name: img.name,
+            content: img,
+          })),
+        ],
+      }));
+    },
+    file: (e: ChangeEvent<HTMLInputElement>) => {
+      const files = Array.from(e.target.files || []);
+      setDocList((prev) => ({
+        ...prev,
+        file: [
+          ...prev.file,
+          ...files.map((file) => ({
+            name: file.name,
+            content: file,
+          })),
+        ],
+      }));
+    },
+    url: () => {
+      if (urlInputRef.current?.value) {
+        const url = urlInputRef.current.value;
+        setDocList((prev) => ({
+          ...prev,
+          url: [
+            ...prev.url,
+            {
+              name: url,
+              content: url,
+            },
+          ],
+        }));
+        urlInputRef.current.value = '';
+      }
+    },
+  };
+
+  const handleAddDoc = {
+    img: () => {
+      imgInputRef.current?.click();
+    },
+    file: () => {
+      fileInputRef.current?.click();
+    },
+    url: () => {
+      handleGetDoc.url();
+    },
+  };
+
+  const handleRemoveDoc = (index: number) => {
+    setDocList((prev) => {
+      const newList = { ...prev };
+      newList[doctype] = newList[doctype].filter((_, idx) => idx !== index);
+      return newList;
+    });
+  };
+
+  return (
+    <ActionModal
+      isOpen={isOpen}
+      size="xl"
+      onClose={onClose}
+      title="학습자료 등록"
+      subButtonText="취소"
+      onSubButtonClick={onClose}
+      mainButtonText="등록"
+      onMainButtonClick={onConfirmButtonClick}
+    >
+      <Flex direction="column" gap="4">
+        <Text textStyle="bold_xl">학습자료 제목</Text>
+        <Input placeholder="학습자료 제목을 입력해주세요." />
+        <Text textStyle="bold_xl">학습자료 소개</Text>
+        <Textarea placeholder="학습자료 소개를 입력해주세요." />
+        <StyledRadioGroup title="파일 유형" value={doctype} onChange={(v) => setDocType(v as DocumentType)}>
+          <StyledRadio value="img">이미지</StyledRadio>
+          <StyledRadio value="file">파일</StyledRadio>
+          <StyledRadio value="url">URL 링크</StyledRadio>
+        </StyledRadioGroup>
+        <Flex justify="end" direction="row" gap="4" shrink="0">
+          <Input
+            ref={urlInputRef}
+            flex="1"
+            h="7"
+            shadow="md"
+            hidden={doctype !== 'url'}
+            placeholder="URL 링크를 입력해주세요."
+          />
+          <Button w="28" h="7" shadow="md" onClick={() => handleAddDoc[doctype]()} variant="orange">
+            추가하기
+          </Button>
+        </Flex>
+        <Divider borderWidth="2px" borderColor={color.orange_dark} />
+        <input
+          hidden
+          type="file"
+          multiple
+          accept="image/jpg,image/png,image/jpeg,image/gif"
+          ref={imgInputRef}
+          onChange={handleGetDoc.img}
+        />
+        <input hidden type="file" multiple ref={fileInputRef} onChange={handleGetDoc.file} />
+        <Flex direction="column" gap="4" overflow="scroll" maxH="52" shrink="0">
+          {docList[doctype].map((doc, index) => (
+            <IconBox
+              key={`${doc}`}
+              leftIcon={DocumentBoxIcon[doctype]}
+              content={doc.name}
+              rightIcon={<BiTrash />}
+              handleClick={() => handleRemoveDoc(index)}
+            />
+          ))}
+        </Flex>
+        <StyledRadioGroup title="공개 범위" defaultValue="all">
+          <StyledRadio value="all">전체 공개</StyledRadio>
+          <StyledRadio value="only_study">스터디 공개</StyledRadio>
+        </StyledRadioGroup>
+      </Flex>
+    </ActionModal>
+  );
+};
+
+export default DocumentModal;
diff --git a/src/containers/study/DocumentModal/type.ts b/src/containers/study/DocumentModal/type.ts
new file mode 100644
index 00000000..b29df881
--- /dev/null
+++ b/src/containers/study/DocumentModal/type.ts
@@ -0,0 +1,15 @@
+export interface DocumentModalProps {
+  isOpen: boolean;
+  onClose: () => void;
+}
+
+export type DocumentType = 'img' | 'file' | 'url';
+
+interface Document {
+  name: string;
+  content: string | File;
+}
+
+export type DocumentList = {
+  [key in DocumentType]: Array<Document>;
+};