diff --git a/pages/main/index.tsx b/pages/main/index.tsx index ec6e249b..be3868e0 100644 --- a/pages/main/index.tsx +++ b/pages/main/index.tsx @@ -11,6 +11,7 @@ import useGetWriteHistory from '@domain/끄적이는/queries/useGetHistory'; import * as styles from '@domain/끄적이는/style.css'; import 참고하는TabContent from '@domain/참고하는/components'; import { useInput } from '@hooks/useInput'; +import useGetMemoFolders from '@queries/useGetMemoFolders'; import useGetMyProfile from '@queries/useGetMyProfile'; import { COLORS } from '@styles/tokens'; @@ -20,6 +21,7 @@ const MainPage = () => { const writeInput = useInput({ id: 'write-input' }); const { todayMemos, history } = useGetWriteHistory(); const { mutate: submitTemporalMemo } = useCreateTemporalMemo(); + const { data: memoFolders } = useGetMemoFolders(); const [selectedTab, setSelectedTab] = useState('끄적이는'); @@ -27,6 +29,10 @@ const MainPage = () => { setSelectedTab(selectedTab); }; + const handleSubmit = () => { + submitTemporalMemo(writeInput.value); + }; + const backgroundColor = selectedTab === '참고하는' ? COLORS['Grey/100'] : undefined; @@ -37,13 +43,16 @@ const MainPage = () => {
- - + +
- submitTemporalMemo(writeInput.value)} - /> +
diff --git a/src/components/Dropdown/FolderDropdown/index.tsx b/src/components/Dropdown/FolderDropdown/index.tsx new file mode 100644 index 00000000..2ec4a123 --- /dev/null +++ b/src/components/Dropdown/FolderDropdown/index.tsx @@ -0,0 +1,67 @@ +import { type Folder } from '@api/memoFolder/types'; +import Button from '@components/Button'; +import Dropdown from '@components/Dropdown'; +import Icon from '@components/Icon'; +import useGetMyProfile from '@queries/useGetMyProfile'; +import { useModalStore } from '@stores/modal'; +import { COLORS } from '@styles/tokens'; + +import * as styles from './style.css'; + +interface FolderDropdownProps { + isArchived: boolean; + memoFolders: Folder[]; + onClickFolder: (id: Folder['id']) => void; + onClickBookmark: () => void; +} + +const FolderDropdown = ({ + isArchived, + memoFolders, + onClickFolder, + onClickBookmark, +}: FolderDropdownProps) => { + const { openModal } = useModalStore(); + + const { data } = useGetMyProfile(); + + if (isArchived) { + return ( + + ); + } + + return ( + + + + + + {memoFolders.map(({ id, name }) => ( + onClickFolder(id)}> + {name === '기본' ? ( + + {data?.nickname}님의 폴더 + 기본 + + ) : ( + {name} + )} + + ))} + openModal('makeFolder')}> + + 새 폴더 만들기 + + + + ); +}; + +export default FolderDropdown; diff --git "a/src/domain/\354\260\270\352\263\240\355\225\230\353\212\224/components/FolderDialog.css.ts" b/src/components/Dropdown/FolderDropdown/style.css.ts similarity index 84% rename from "src/domain/\354\260\270\352\263\240\355\225\230\353\212\224/components/FolderDialog.css.ts" rename to src/components/Dropdown/FolderDropdown/style.css.ts index 54276cd8..e486128f 100644 --- "a/src/domain/\354\260\270\352\263\240\355\225\230\353\212\224/components/FolderDialog.css.ts" +++ b/src/components/Dropdown/FolderDropdown/style.css.ts @@ -27,7 +27,7 @@ export const iconMediumText = style([ }), { color: COLORS['Grey/400'], - marginLeft: '28px', + marginLeft: '4px', }, ]); @@ -42,10 +42,3 @@ export const hover = style({ fill: COLORS['Grey/600'], }, }); - -export const hoverBlue = style({ - transition: 'fill 100ms ease-in-out', - ':hover': { - fill: COLORS['Blue/Default'], - }, -}); diff --git a/src/components/Dropdown/style.css.ts b/src/components/Dropdown/style.css.ts index 60b042c3..c06f8c83 100644 --- a/src/components/Dropdown/style.css.ts +++ b/src/components/Dropdown/style.css.ts @@ -22,11 +22,13 @@ export const menuList = recipe({ position: 'absolute', top, left, + maxHeight: '136px', + overflowY: 'scroll', marginTop: '4px', borderRadius: '12px', boxShadow: '0px 8px 15px 0px rgba(28, 28, 28, 0.08)', backgroundColor: COLORS['Grey/White'], - zIndex: 50, + zIndex: 80, }, variants: { size: { diff --git a/src/components/Layout/MainPageTab.tsx b/src/components/Layout/MainPageTab.tsx index 0b5bf652..357ef268 100644 --- a/src/components/Layout/MainPageTab.tsx +++ b/src/components/Layout/MainPageTab.tsx @@ -1,6 +1,7 @@ import { type ReactNode } from 'react'; import Tabs from '@components/Tabs'; +import Tooltip from '@components/Tooltip'; import * as styles from './style.css'; @@ -25,18 +26,28 @@ const MainPageTab = ({ >
- - 끄적이는 - - - 참고하는 - + + + + 끄적이는 + + + 문장을 끄적이고 검사해봐요 + + + + + 참고하는 + + + 문장 템플릿을 찾아봐요 +
diff --git a/src/components/Responsive/index.tsx b/src/components/Responsive/index.tsx index b09fd2ba..68b016dd 100644 --- a/src/components/Responsive/index.tsx +++ b/src/components/Responsive/index.tsx @@ -10,7 +10,7 @@ interface ResponsiveProps { const Responsive = ({ children, className, - columnsCountBreakPoints = { 768: 2, 1080: 3 }, + columnsCountBreakPoints = { 0: 1, 768: 2, 1080: 3 }, }: PropsWithChildren) => { return ( { const { showToast } = useToastStore(); + const { mutate: updateTemporalMemo } = useEditTemporalMemo(); const { mutate: deleteTemporalMemo } = useDeleteTemporalMemo(); + const { mutate: saveTemporalMemo } = useSaveTemporalMemo(); + const editedInputProps = useInput({ id: 'edit-input', defaultValue: content, @@ -47,6 +58,12 @@ const WriteHistoryCard = ({ setTimeout(() => onEditCompleteClick(), 0); }; + const handleFolderClick = (memoFolderId: Folder['id']) => { + saveTemporalMemo({ temporalMemoId: id, memoFolderId }); + }; + + const handleBookmarkClick = () => {}; + if (isEditMode) { return (
  • @@ -67,36 +84,27 @@ const WriteHistoryCard = ({ } return ( -
  • -
    -

    - {dayjs(createdAt).locale('ko').format('a h:mm')} -

    -
    - - - deleteTemporalMemo(id)} - /> -
    -
    -

    {content}

    -
  • + + + {dayjs(createdAt).locale('ko').format('a h:mm')} + + + + + deleteTemporalMemo(id)} + /> + + {content} + ); }; diff --git "a/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/components/Card/Today/index.tsx" "b/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/components/Card/Today/index.tsx" index b2cda72c..4012bb48 100644 --- "a/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/components/Card/Today/index.tsx" +++ "b/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/components/Card/Today/index.tsx" @@ -1,14 +1,18 @@ import { useState } from 'react'; import dayjs from 'dayjs'; +import { type Folder } from '@api/memoFolder/types'; import { type SpellCheckResponse } from '@api/spell/types'; import Button from '@components/Button'; import Card from '@components/Card'; +import FolderDropdown from '@components/Dropdown/FolderDropdown'; import MenuDropdown from '@components/Dropdown/MenuDropdown'; import Icon from '@components/Icon'; import SkeletonContent from '@components/Loading/Skeleton/SkeletonContent'; import useDeleteTemporalMemo from '@domain/끄적이는/mutations/useDeleteTemporalMemo'; import useEditTemporalMemo from '@domain/끄적이는/mutations/useEditTemporalMemo'; +import useSaveTemporalMemo from '@domain/끄적이는/mutations/useSaveTemporalMemo'; +import { type TemporalMemo } from '@domain/끄적이는/types'; import { useInput } from '@hooks/useInput'; import usePostSpellCheck from '@queries/usePostSpellCheck'; import { useToastStore } from '@stores/toast'; @@ -18,18 +22,16 @@ import SpellCheckCard from '../../Today/components/SpellCheckCard'; import * as styles from './style.css'; interface WriteTodayCardProps { - id: number; - createAt: string; - content: string; + memo: TemporalMemo; + memoFolders: Folder[]; isEditMode: boolean; onEditClick: (id: number) => void; onEditCompleteClick: VoidFunction; } const WriteTodayCard = ({ - id, - createAt, - content, + memo, + memoFolders, isEditMode, onEditClick, onEditCompleteClick, @@ -37,10 +39,11 @@ const WriteTodayCard = ({ const { showToast } = useToastStore(); const { mutate: updateTemporalMemo } = useEditTemporalMemo(); const { mutate: deleteTemporalMemo } = useDeleteTemporalMemo(); + const { mutate: saveTemporalMemo } = useSaveTemporalMemo(); const editInputProps = useInput({ id: 'edit-today-input', - defaultValue: content, + defaultValue: memo.content, }); const [spellCheckResult, setSpellCheckResult] = @@ -53,14 +56,14 @@ const WriteTodayCard = ({ const handleSpellCheck = async () => { const spellCheckResult = await spellCheck({ - sentence: content, + sentence: memo.content, }); setSpellCheckResult(spellCheckResult); }; const handleCopyClick = () => { - navigator.clipboard.writeText(content); + navigator.clipboard.writeText(memo.content); showToast({ message: '문장이 복사되었어요. 원하는 곳에 붙여넣기(Ctrl+V)를 해주세요!', }); @@ -68,15 +71,19 @@ const WriteTodayCard = ({ //TODO: 밸리데이션 추가 const handleUpdate = () => { - updateTemporalMemo({ id: id, content: editInputProps.value }); + updateTemporalMemo({ id: memo.id, content: editInputProps.value }); setTimeout(() => onEditCompleteClick(), 0); }; + const handleFolderClick = (memoFolderId: Folder['id']) => { + saveTemporalMemo({ temporalMemoId: memo.id, memoFolderId }); + }; + if (isEditMode) { return ( - {dayjs(createAt).locale('ko').format('a h:mm')} + {dayjs(memo.createdAt).locale('ko').format('a h:mm')} @@ -98,18 +105,23 @@ const WriteTodayCard = ({ - + {}} + /> onEditClick(id)} - onDelete={() => deleteTemporalMemo(id)} + onEdit={() => onEditClick(memo.id)} + onDelete={() => deleteTemporalMemo(memo.id)} /> )} - {dayjs(createAt).locale('ko').format('a h:mm')} + + {dayjs(memo.createdAt).locale('ko').format('a h:mm')} + -

    {content}

    +

    {memo.content}

    {isPendingSpellCheck && (
    diff --git "a/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/components/History/Column/index.tsx" "b/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/components/History/Column/index.tsx" deleted file mode 100644 index 1d89551a..00000000 --- "a/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/components/History/Column/index.tsx" +++ /dev/null @@ -1,38 +0,0 @@ -import { type TemporalMemo } from '@domain/끄적이는/types'; - -import WriteHistoryCard from '../../Card/History'; -import * as styles from './style.css'; - -interface ColumnProps { - list: TemporalMemo[] | undefined; - editModeCardId: number | null; - resetEditModeCardId: VoidFunction; - onEditClick: (id: number) => void; -} - -const Column = ({ - list, - editModeCardId, - resetEditModeCardId, - onEditClick, -}: ColumnProps) => { - if (!list) { - return null; - } - - return ( -
      - {list.map((el) => ( - onEditClick(el.id)} - onEditCompleteClick={resetEditModeCardId} - /> - ))} -
    - ); -}; - -export default Column; diff --git "a/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/components/History/Column/style.css.ts" "b/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/components/History/Column/style.css.ts" deleted file mode 100644 index f1397b7e..00000000 --- "a/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/components/History/Column/style.css.ts" +++ /dev/null @@ -1,8 +0,0 @@ -import { style } from '@vanilla-extract/css'; - -export const container = style({ - display: 'flex', - flexDirection: 'column', - gap: '16px', - flex: '1', -}); diff --git "a/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/components/History/index.css.ts" "b/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/components/History/index.css.ts" index 8b2ca7e6..baf8464d 100644 --- "a/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/components/History/index.css.ts" +++ "b/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/components/History/index.css.ts" @@ -34,8 +34,3 @@ export const dateLabelText = style({ lineHeight: '24px', letterSpacing: '-0.2px', }); - -export const contentWrapper = style({ - display: 'flex', - gap: '16px', -}); diff --git "a/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/components/History/index.tsx" "b/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/components/History/index.tsx" index 5aad787e..2983bafd 100644 --- "a/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/components/History/index.tsx" +++ "b/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/components/History/index.tsx" @@ -3,36 +3,23 @@ import dayjs from 'dayjs'; import 'dayjs/locale/ko'; +import { type Folder } from '@api/memoFolder/types'; import Icon from '@components/Icon'; +import Responsive from '@components/Responsive'; -import { type TemporalMemo, type TemporalMemoHistory } from '../../types'; -import Column from './Column'; +import { type TemporalMemoHistory } from '../../types'; +import WriteHistoryCard from '../Card/History'; import * as styles from './index.css'; interface TemporalMemoHistoryTableProps { data: TemporalMemoHistory[]; + memoFolders: Folder[]; } -const parser = (arr: TemporalMemo[]): TemporalMemo[][] => { - let queue1: TemporalMemo[] = []; - let queue2: TemporalMemo[] = []; - let queue3: TemporalMemo[] = []; - - arr.forEach((history, i) => { - const extra = i % 3; - if (extra === 0) { - queue1.push(history); - } else if (extra === 1) { - queue2.push(history); - } else { - queue3.push(history); - } - }); - - return [queue1, queue2, queue3]; -}; - -const TemporalMemoHistoryTable = ({ data }: TemporalMemoHistoryTableProps) => { +const TemporalMemoHistoryTable = ({ + data, + memoFolders, +}: TemporalMemoHistoryTableProps) => { const [editModeCardId, setEditModeCardId] = useState(null); return ( @@ -50,22 +37,18 @@ const TemporalMemoHistoryTable = ({ data }: TemporalMemoHistoryTableProps) => {
    - -
    - {parser(temporalMemos).map((list, i) => { - return ( - { - setEditModeCardId(id); - }} - resetEditModeCardId={() => setEditModeCardId(null)} - /> - ); - })} -
    + + {temporalMemos.map((temporalMemo) => ( + setEditModeCardId(temporalMemo.id)} + onEditCompleteClick={() => setEditModeCardId(null)} + /> + ))} + ); })} diff --git "a/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/components/Today/components/SpellCheckNotice/style.css.ts" "b/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/components/Today/components/SpellCheckNotice/style.css.ts" index 5e8183b7..5a4f7e18 100644 --- "a/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/components/Today/components/SpellCheckNotice/style.css.ts" +++ "b/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/components/Today/components/SpellCheckNotice/style.css.ts" @@ -8,6 +8,7 @@ export const notice = style([ utils.flexAlignCenter, sprinkles({ typography: '13/Title/Semibold' }), { + marginTop: '16px', color: COLORS['Blue/Dark'], gap: '4px', }, diff --git "a/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/components/Today/index.css.ts" "b/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/components/Today/index.css.ts" index db50bdcc..a22dbcee 100644 --- "a/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/components/Today/index.css.ts" +++ "b/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/components/Today/index.css.ts" @@ -6,6 +6,7 @@ export const container = style({ display: 'flex', flexDirection: 'column', gap: '20px', + marginBottom: '40px', }); export const dateLabelWrapper = style({ diff --git "a/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/components/Today/index.tsx" "b/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/components/Today/index.tsx" index 942f312a..d8075248 100644 --- "a/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/components/Today/index.tsx" +++ "b/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/components/Today/index.tsx" @@ -1,5 +1,6 @@ import { useState } from 'react'; +import { type Folder } from '@api/memoFolder/types'; import Icon from '@components/Icon'; import { type TemporalMemo } from '../../types'; @@ -8,9 +9,10 @@ import * as styles from './index.css'; interface TodayTemoralMemosProps { memos: TemporalMemo[]; + memoFolders: Folder[]; } -const TodayTemoralMemos = ({ memos }: TodayTemoralMemosProps) => { +const TodayTemoralMemos = ({ memos, memoFolders }: TodayTemoralMemosProps) => { const [editModeCardId, setEditModeCardId] = useState(null); if (!memos || memos.length === 0) { @@ -29,9 +31,8 @@ const TodayTemoralMemos = ({ memos }: TodayTemoralMemosProps) => { {memos.map((memo) => ( setEditModeCardId(id)} onEditCompleteClick={() => setEditModeCardId(null)} diff --git "a/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/mutations/useSaveTemporalMemo.ts" "b/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/mutations/useSaveTemporalMemo.ts" new file mode 100644 index 00000000..e5f6f526 --- /dev/null +++ "b/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/mutations/useSaveTemporalMemo.ts" @@ -0,0 +1,34 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query'; + +import { http } from '@api/http'; +import { useToastStore } from '@stores/toast'; + +import { TemporalMemoQueryKeys } from '../constants/queryKeys'; + +const saveTemporalMemo = async ({ + temporalMemoId, + memoFolderId, +}: { + temporalMemoId: number; + memoFolderId: number; +}) => { + await http.post(`/temporal-memos/${temporalMemoId}/archive`, { + memoFolderId, + }); +}; + +const useSaveTemporalMemo = () => { + const queryClient = useQueryClient(); + + const { showToast } = useToastStore(); + + return useMutation({ + mutationFn: saveTemporalMemo, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: TemporalMemoQueryKeys.all }); + showToast({ message: '글이 저장됐어요.' }); + }, + }); +}; + +export default useSaveTemporalMemo; diff --git "a/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/style.css.ts" "b/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/style.css.ts" index 2d62ff24..4b01b23e 100644 --- "a/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/style.css.ts" +++ "b/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/style.css.ts" @@ -6,19 +6,29 @@ export const container = style({ flexDirection: 'column', justifyContent: 'flex-end', marginBottom: '56px', + padding: '0 40px', }); export const content = style({ overflow: 'scroll', + paddingBottom: '48px', + '::-webkit-scrollbar': { display: 'none', }, - paddingBottom: '48px', }); export const inputWrapper = style({ position: 'fixed', + left: '50%', bottom: '56px', width: '100%', - maxWidth: '1200px', + transform: 'translateX(-50%)', + maxWidth: '1140px', + + '@media': { + 'screen and (max-width: 1200px)': { + padding: '0 40px', + }, + }, }); diff --git "a/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/types/index.ts" "b/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/types/index.ts" index 440dadf5..1e8ba4be 100644 --- "a/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/types/index.ts" +++ "b/src/domain/\353\201\204\354\240\201\354\235\264\353\212\224/types/index.ts" @@ -1,7 +1,7 @@ export interface TemporalMemo { id: number; content: string; - correctionContent: string | null; + correctionContent?: string; isCorrected: boolean; isArchived: boolean; createdAt: string; diff --git "a/src/domain/\354\240\200\354\236\245\355\225\230\353\212\224/components/ArchiveFolder/index.tsx" "b/src/domain/\354\240\200\354\236\245\355\225\230\353\212\224/components/ArchiveFolder/index.tsx" index eedcce00..cb28b5a2 100644 --- "a/src/domain/\354\240\200\354\236\245\355\225\230\353\212\224/components/ArchiveFolder/index.tsx" +++ "b/src/domain/\354\240\200\354\236\245\355\225\230\353\212\224/components/ArchiveFolder/index.tsx" @@ -1,5 +1,9 @@ +import Link from 'next/link'; +import { useRouter } from 'next/router'; + import Button from '@components/Button'; import Icon from '@components/Icon'; +import { ROUTES } from '@constants/routes'; import { type Folder } from '@domain/저장하는/types'; import useGetMyProfile from '@queries/useGetMyProfile'; import { useModalStore } from '@stores/modal'; @@ -12,18 +16,30 @@ interface ArchiveFolderProps { } const ArchiveFolder = ({ folders }: ArchiveFolderProps) => { + const router = useRouter(); + const { openModal } = useModalStore(); const { data } = useGetMyProfile(); return (