diff --git a/.pnp.cjs b/.pnp.cjs index ac99df7..71b5d83 100755 --- a/.pnp.cjs +++ b/.pnp.cjs @@ -34,6 +34,8 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["@testing-library/jest-dom", "npm:5.17.0"],\ ["@testing-library/react", "virtual:9d3f337d4ce8babbc193c806d4d9e413a867cad38593f4b1e990f4e182ddc8ed767147170cefc5df554999b9364f7479a97c29d84c6817d8e0d184881252dbf3#npm:13.4.0"],\ ["@testing-library/user-event", "virtual:9d3f337d4ce8babbc193c806d4d9e413a867cad38593f4b1e990f4e182ddc8ed767147170cefc5df554999b9364f7479a97c29d84c6817d8e0d184881252dbf3#npm:13.5.0"],\ + ["@toast-ui/editor", "npm:3.2.2"],\ + ["@toast-ui/react-editor", "virtual:9d3f337d4ce8babbc193c806d4d9e413a867cad38593f4b1e990f4e182ddc8ed767147170cefc5df554999b9364f7479a97c29d84c6817d8e0d184881252dbf3#npm:3.2.3"],\ ["@types/jest", "npm:27.5.2"],\ ["@types/node", "npm:16.18.80"],\ ["@types/react", "npm:18.2.55"],\ @@ -4981,6 +4983,48 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["@toast-ui/editor", [\ + ["npm:3.2.2", {\ + "packageLocation": "./.yarn/cache/@toast-ui-editor-npm-3.2.2-8b6a9786fd-b1772a1a15.zip/node_modules/@toast-ui/editor/",\ + "packageDependencies": [\ + ["@toast-ui/editor", "npm:3.2.2"],\ + ["dompurify", "npm:2.5.7"],\ + ["prosemirror-commands", "npm:1.6.0"],\ + ["prosemirror-history", "npm:1.4.1"],\ + ["prosemirror-inputrules", "npm:1.4.0"],\ + ["prosemirror-keymap", "npm:1.2.2"],\ + ["prosemirror-model", "npm:1.22.3"],\ + ["prosemirror-state", "npm:1.4.3"],\ + ["prosemirror-transform", "npm:1.10.0"],\ + ["prosemirror-view", "npm:1.34.3"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@toast-ui/react-editor", [\ + ["npm:3.2.3", {\ + "packageLocation": "./.yarn/cache/@toast-ui-react-editor-npm-3.2.3-aed61aa676-5ffd6f9b27.zip/node_modules/@toast-ui/react-editor/",\ + "packageDependencies": [\ + ["@toast-ui/react-editor", "npm:3.2.3"]\ + ],\ + "linkType": "SOFT"\ + }],\ + ["virtual:9d3f337d4ce8babbc193c806d4d9e413a867cad38593f4b1e990f4e182ddc8ed767147170cefc5df554999b9364f7479a97c29d84c6817d8e0d184881252dbf3#npm:3.2.3", {\ + "packageLocation": "./.yarn/__virtual__/@toast-ui-react-editor-virtual-529516fcf7/0/cache/@toast-ui-react-editor-npm-3.2.3-aed61aa676-5ffd6f9b27.zip/node_modules/@toast-ui/react-editor/",\ + "packageDependencies": [\ + ["@toast-ui/react-editor", "virtual:9d3f337d4ce8babbc193c806d4d9e413a867cad38593f4b1e990f4e182ddc8ed767147170cefc5df554999b9364f7479a97c29d84c6817d8e0d184881252dbf3#npm:3.2.3"],\ + ["@toast-ui/editor", "npm:3.2.2"],\ + ["@types/react", "npm:18.2.55"],\ + ["prosemirror-transform", "npm:1.10.0"],\ + ["react", "npm:18.2.0"]\ + ],\ + "packagePeers": [\ + "@types/react",\ + "react"\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["@tootallnate/once", [\ ["npm:1.1.2", {\ "packageLocation": "./.yarn/cache/@tootallnate-once-npm-1.1.2-0517220057-e1fb1bbbc1.zip/node_modules/@tootallnate/once/",\ @@ -8748,6 +8792,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["dompurify", [\ + ["npm:2.5.7", {\ + "packageLocation": "./.yarn/cache/dompurify-npm-2.5.7-18b27adcf8-9652139743.zip/node_modules/dompurify/",\ + "packageDependencies": [\ + ["dompurify", "npm:2.5.7"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["domutils", [\ ["npm:1.7.0", {\ "packageLocation": "./.yarn/cache/domutils-npm-1.7.0-7a1529fcfc-f60a725b1f.zip/node_modules/domutils/",\ @@ -12753,6 +12806,8 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["@testing-library/jest-dom", "npm:5.17.0"],\ ["@testing-library/react", "virtual:9d3f337d4ce8babbc193c806d4d9e413a867cad38593f4b1e990f4e182ddc8ed767147170cefc5df554999b9364f7479a97c29d84c6817d8e0d184881252dbf3#npm:13.4.0"],\ ["@testing-library/user-event", "virtual:9d3f337d4ce8babbc193c806d4d9e413a867cad38593f4b1e990f4e182ddc8ed767147170cefc5df554999b9364f7479a97c29d84c6817d8e0d184881252dbf3#npm:13.5.0"],\ + ["@toast-ui/editor", "npm:3.2.2"],\ + ["@toast-ui/react-editor", "virtual:9d3f337d4ce8babbc193c806d4d9e413a867cad38593f4b1e990f4e182ddc8ed767147170cefc5df554999b9364f7479a97c29d84c6817d8e0d184881252dbf3#npm:3.2.3"],\ ["@types/jest", "npm:27.5.2"],\ ["@types/node", "npm:16.18.80"],\ ["@types/react", "npm:18.2.55"],\ @@ -13838,6 +13893,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["orderedmap", [\ + ["npm:2.1.1", {\ + "packageLocation": "./.yarn/cache/orderedmap-npm-2.1.1-64b8a0684f-082cf970b0.zip/node_modules/orderedmap/",\ + "packageDependencies": [\ + ["orderedmap", "npm:2.1.1"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["p-limit", [\ ["npm:2.3.0", {\ "packageLocation": "./.yarn/cache/p-limit-npm-2.3.0-94a0310039-84ff17f1a3.zip/node_modules/p-limit/",\ @@ -15943,6 +16007,97 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["prosemirror-commands", [\ + ["npm:1.6.0", {\ + "packageLocation": "./.yarn/cache/prosemirror-commands-npm-1.6.0-ec2429d6e5-75c965e196.zip/node_modules/prosemirror-commands/",\ + "packageDependencies": [\ + ["prosemirror-commands", "npm:1.6.0"],\ + ["prosemirror-model", "npm:1.22.3"],\ + ["prosemirror-state", "npm:1.4.3"],\ + ["prosemirror-transform", "npm:1.10.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["prosemirror-history", [\ + ["npm:1.4.1", {\ + "packageLocation": "./.yarn/cache/prosemirror-history-npm-1.4.1-ad68c86bb7-90f9bf59bc.zip/node_modules/prosemirror-history/",\ + "packageDependencies": [\ + ["prosemirror-history", "npm:1.4.1"],\ + ["prosemirror-state", "npm:1.4.3"],\ + ["prosemirror-transform", "npm:1.10.0"],\ + ["prosemirror-view", "npm:1.34.3"],\ + ["rope-sequence", "npm:1.3.4"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["prosemirror-inputrules", [\ + ["npm:1.4.0", {\ + "packageLocation": "./.yarn/cache/prosemirror-inputrules-npm-1.4.0-14c674c300-b893eff382.zip/node_modules/prosemirror-inputrules/",\ + "packageDependencies": [\ + ["prosemirror-inputrules", "npm:1.4.0"],\ + ["prosemirror-state", "npm:1.4.3"],\ + ["prosemirror-transform", "npm:1.10.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["prosemirror-keymap", [\ + ["npm:1.2.2", {\ + "packageLocation": "./.yarn/cache/prosemirror-keymap-npm-1.2.2-efb8276b00-85fe4fc303.zip/node_modules/prosemirror-keymap/",\ + "packageDependencies": [\ + ["prosemirror-keymap", "npm:1.2.2"],\ + ["prosemirror-state", "npm:1.4.3"],\ + ["w3c-keyname", "npm:2.2.8"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["prosemirror-model", [\ + ["npm:1.22.3", {\ + "packageLocation": "./.yarn/cache/prosemirror-model-npm-1.22.3-f92a11e7ca-dfc1f80fad.zip/node_modules/prosemirror-model/",\ + "packageDependencies": [\ + ["prosemirror-model", "npm:1.22.3"],\ + ["orderedmap", "npm:2.1.1"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["prosemirror-state", [\ + ["npm:1.4.3", {\ + "packageLocation": "./.yarn/cache/prosemirror-state-npm-1.4.3-d6540e44ad-28857d935c.zip/node_modules/prosemirror-state/",\ + "packageDependencies": [\ + ["prosemirror-state", "npm:1.4.3"],\ + ["prosemirror-model", "npm:1.22.3"],\ + ["prosemirror-transform", "npm:1.10.0"],\ + ["prosemirror-view", "npm:1.34.3"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["prosemirror-transform", [\ + ["npm:1.10.0", {\ + "packageLocation": "./.yarn/cache/prosemirror-transform-npm-1.10.0-c36ebced12-4cacb4ef83.zip/node_modules/prosemirror-transform/",\ + "packageDependencies": [\ + ["prosemirror-transform", "npm:1.10.0"],\ + ["prosemirror-model", "npm:1.22.3"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["prosemirror-view", [\ + ["npm:1.34.3", {\ + "packageLocation": "./.yarn/cache/prosemirror-view-npm-1.34.3-c79b7cffaa-622a9e4061.zip/node_modules/prosemirror-view/",\ + "packageDependencies": [\ + ["prosemirror-view", "npm:1.34.3"],\ + ["prosemirror-model", "npm:1.22.3"],\ + ["prosemirror-state", "npm:1.4.3"],\ + ["prosemirror-transform", "npm:1.10.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["proxy-addr", [\ ["npm:2.0.7", {\ "packageLocation": "./.yarn/cache/proxy-addr-npm-2.0.7-dae6552872-29c6990ce9.zip/node_modules/proxy-addr/",\ @@ -17870,6 +18025,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["rope-sequence", [\ + ["npm:1.3.4", {\ + "packageLocation": "./.yarn/cache/rope-sequence-npm-1.3.4-3b09587e5f-95cca2f99a.zip/node_modules/rope-sequence/",\ + "packageDependencies": [\ + ["rope-sequence", "npm:1.3.4"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["run-parallel", [\ ["npm:1.2.0", {\ "packageLocation": "./.yarn/cache/run-parallel-npm-1.2.0-3f47ff2034-cb4f97ad25.zip/node_modules/run-parallel/",\ @@ -19779,6 +19943,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["w3c-keyname", [\ + ["npm:2.2.8", {\ + "packageLocation": "./.yarn/cache/w3c-keyname-npm-2.2.8-66d7d5317a-95bafa4c04.zip/node_modules/w3c-keyname/",\ + "packageDependencies": [\ + ["w3c-keyname", "npm:2.2.8"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["w3c-xmlserializer", [\ ["npm:2.0.0", {\ "packageLocation": "./.yarn/cache/w3c-xmlserializer-npm-2.0.0-f8f7bc8b42-ae25c51cf7.zip/node_modules/w3c-xmlserializer/",\ diff --git a/.yarn/cache/@toast-ui-editor-npm-3.2.2-8b6a9786fd-b1772a1a15.zip b/.yarn/cache/@toast-ui-editor-npm-3.2.2-8b6a9786fd-b1772a1a15.zip new file mode 100644 index 0000000..bcf09fc Binary files /dev/null and b/.yarn/cache/@toast-ui-editor-npm-3.2.2-8b6a9786fd-b1772a1a15.zip differ diff --git a/.yarn/cache/@toast-ui-react-editor-npm-3.2.3-aed61aa676-5ffd6f9b27.zip b/.yarn/cache/@toast-ui-react-editor-npm-3.2.3-aed61aa676-5ffd6f9b27.zip new file mode 100644 index 0000000..74438ca Binary files /dev/null and b/.yarn/cache/@toast-ui-react-editor-npm-3.2.3-aed61aa676-5ffd6f9b27.zip differ diff --git a/.yarn/cache/dompurify-npm-2.5.7-18b27adcf8-9652139743.zip b/.yarn/cache/dompurify-npm-2.5.7-18b27adcf8-9652139743.zip new file mode 100644 index 0000000..a496046 Binary files /dev/null and b/.yarn/cache/dompurify-npm-2.5.7-18b27adcf8-9652139743.zip differ diff --git a/.yarn/cache/orderedmap-npm-2.1.1-64b8a0684f-082cf970b0.zip b/.yarn/cache/orderedmap-npm-2.1.1-64b8a0684f-082cf970b0.zip new file mode 100644 index 0000000..2623178 Binary files /dev/null and b/.yarn/cache/orderedmap-npm-2.1.1-64b8a0684f-082cf970b0.zip differ diff --git a/.yarn/cache/prosemirror-commands-npm-1.6.0-ec2429d6e5-75c965e196.zip b/.yarn/cache/prosemirror-commands-npm-1.6.0-ec2429d6e5-75c965e196.zip new file mode 100644 index 0000000..99774d3 Binary files /dev/null and b/.yarn/cache/prosemirror-commands-npm-1.6.0-ec2429d6e5-75c965e196.zip differ diff --git a/.yarn/cache/prosemirror-history-npm-1.4.1-ad68c86bb7-90f9bf59bc.zip b/.yarn/cache/prosemirror-history-npm-1.4.1-ad68c86bb7-90f9bf59bc.zip new file mode 100644 index 0000000..81a3f83 Binary files /dev/null and b/.yarn/cache/prosemirror-history-npm-1.4.1-ad68c86bb7-90f9bf59bc.zip differ diff --git a/.yarn/cache/prosemirror-inputrules-npm-1.4.0-14c674c300-b893eff382.zip b/.yarn/cache/prosemirror-inputrules-npm-1.4.0-14c674c300-b893eff382.zip new file mode 100644 index 0000000..d4a8acd Binary files /dev/null and b/.yarn/cache/prosemirror-inputrules-npm-1.4.0-14c674c300-b893eff382.zip differ diff --git a/.yarn/cache/prosemirror-keymap-npm-1.2.2-efb8276b00-85fe4fc303.zip b/.yarn/cache/prosemirror-keymap-npm-1.2.2-efb8276b00-85fe4fc303.zip new file mode 100644 index 0000000..a22e042 Binary files /dev/null and b/.yarn/cache/prosemirror-keymap-npm-1.2.2-efb8276b00-85fe4fc303.zip differ diff --git a/.yarn/cache/prosemirror-model-npm-1.22.3-f92a11e7ca-dfc1f80fad.zip b/.yarn/cache/prosemirror-model-npm-1.22.3-f92a11e7ca-dfc1f80fad.zip new file mode 100644 index 0000000..760e40f Binary files /dev/null and b/.yarn/cache/prosemirror-model-npm-1.22.3-f92a11e7ca-dfc1f80fad.zip differ diff --git a/.yarn/cache/prosemirror-state-npm-1.4.3-d6540e44ad-28857d935c.zip b/.yarn/cache/prosemirror-state-npm-1.4.3-d6540e44ad-28857d935c.zip new file mode 100644 index 0000000..5d1d11a Binary files /dev/null and b/.yarn/cache/prosemirror-state-npm-1.4.3-d6540e44ad-28857d935c.zip differ diff --git a/.yarn/cache/prosemirror-transform-npm-1.10.0-c36ebced12-4cacb4ef83.zip b/.yarn/cache/prosemirror-transform-npm-1.10.0-c36ebced12-4cacb4ef83.zip new file mode 100644 index 0000000..08ffb1f Binary files /dev/null and b/.yarn/cache/prosemirror-transform-npm-1.10.0-c36ebced12-4cacb4ef83.zip differ diff --git a/.yarn/cache/prosemirror-view-npm-1.34.3-c79b7cffaa-622a9e4061.zip b/.yarn/cache/prosemirror-view-npm-1.34.3-c79b7cffaa-622a9e4061.zip new file mode 100644 index 0000000..b9f8a2c Binary files /dev/null and b/.yarn/cache/prosemirror-view-npm-1.34.3-c79b7cffaa-622a9e4061.zip differ diff --git a/.yarn/cache/rope-sequence-npm-1.3.4-3b09587e5f-95cca2f99a.zip b/.yarn/cache/rope-sequence-npm-1.3.4-3b09587e5f-95cca2f99a.zip new file mode 100644 index 0000000..07a75fb Binary files /dev/null and b/.yarn/cache/rope-sequence-npm-1.3.4-3b09587e5f-95cca2f99a.zip differ diff --git a/.yarn/cache/w3c-keyname-npm-2.2.8-66d7d5317a-95bafa4c04.zip b/.yarn/cache/w3c-keyname-npm-2.2.8-66d7d5317a-95bafa4c04.zip new file mode 100644 index 0000000..f288646 Binary files /dev/null and b/.yarn/cache/w3c-keyname-npm-2.2.8-66d7d5317a-95bafa4c04.zip differ diff --git a/.yarnrc.yml b/.yarnrc.yml index 8bd2b92..6c49516 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -1,2 +1,10 @@ yarnPath: .yarn/releases/yarn-3.8.0.cjs -nodeLinker: "pnp" \ No newline at end of file +nodeLinker: "pnp" + +packageExtensions: + '@toast-ui/editor@*': + dependencies: + prosemirror-transform: '*' + '@toast-ui/react-editor@*': + dependencies: + prosemirror-transform: '*' \ No newline at end of file diff --git a/package.json b/package.json index fddc444..0ba1afc 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,8 @@ "@testing-library/jest-dom": "^5.14.1", "@testing-library/react": "^13.0.0", "@testing-library/user-event": "^13.2.1", + "@toast-ui/editor": "^3.2.2", + "@toast-ui/react-editor": "^3.2.3", "@types/jest": "^27.0.1", "@types/node": "^16.7.13", "@types/react": "^18.0.0", diff --git a/src/App.tsx b/src/App.tsx index f101f93..49fc7e0 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -23,6 +23,9 @@ import ABTest from 'pages/ABTest'; import ABTestDetail from 'pages/ABTest/components/ABTestDetail'; import ReviewList from 'pages/Services/Review/ReviewList'; import BenefitPage from 'pages/Services/Benefit'; +import NoticeList from 'pages/Services/Notice/NoticeList'; +import NoticeDetail from 'pages/Services/Notice/NoticeDetail'; +import NoticeWrite from 'pages/Services/Notice/NoticeWrite'; function RequireAuth() { const location = useLocation(); @@ -64,6 +67,9 @@ function App() { } /> } /> } /> + } /> + } /> + } /> 404} /> diff --git a/src/components/common/CustomForm/CustomForm.style.tsx b/src/components/common/CustomForm/CustomForm.style.tsx index e017f7f..121615f 100644 --- a/src/components/common/CustomForm/CustomForm.style.tsx +++ b/src/components/common/CustomForm/CustomForm.style.tsx @@ -53,6 +53,50 @@ export const FormItem = styled(Form.Item)` } `; +export const FormTextItem = styled(Form.Item)` + margin-bottom: 0; + .ant-form-item-label > label { + width: 120px; + height: 500px; + display: flex; + align-items: center; + justify-content: center; + font-size: 14px; + font-weight: 600; + color: #2c3e50; + background-color: rgb(243, 243, 243); + border: rgb(217, 217, 217) solid; + border-width: 1px; + border-radius: 0; + &:after { + display: none; + flex-basis: auto; + } + } + // 기본 설정된 반응형에 의한 디자인 깨짐 방지용 + ${mobile}{ + .ant-row .ant-form-item-label { + flex: 0 1 auto; + padding: 0; + } + .ant-row .ant-form-item-control { + flex: 1 1 auto; + padding: 0; + } + } + + .ant-form-item-explain-error { + position: absolute; + right: 12px; + z-index: 10; + top: 5px; + } + + .ant-input-number-input-wrap { + width: 100%; + } +`; + export const FormItemCheckbox = styled(Form.Item)` margin-bottom: 0; @@ -117,3 +161,8 @@ export const UploadWrap = styled(Form.Item)` right: 10vw; } `; + +export const TextWrap = styled.div` + display: flex; + height: 100%; +`; diff --git a/src/components/common/CustomForm/index.tsx b/src/components/common/CustomForm/index.tsx index abd3aa4..410d276 100644 --- a/src/components/common/CustomForm/index.tsx +++ b/src/components/common/CustomForm/index.tsx @@ -4,10 +4,11 @@ import { Input, InputNumberProps, InputProps, Modal, ModalProps, Select, Switch, SwitchProps, } from 'antd'; -import { ReactNode } from 'react'; +import { ReactNode, forwardRef } from 'react'; import { Rule } from 'antd/lib/form'; import useValidate from 'utils/hooks/useValidate'; import { NamePath } from 'antd/lib/form/interface'; +import { Editor, EditorProps, Viewer } from '@toast-ui/react-editor'; import * as S from './CustomForm.style'; import CustomMultipleUpload from './CustomMultipleUpload'; import CustomSingleUpload from './CustomSingleUpload'; @@ -186,6 +187,40 @@ function CustomModal({ ); } +export const CustomEditor = forwardRef(( + { + label, name, rules, ...props + }, + ref, +) => { + return ( + + + + + + + ); +}); + +export const CustomViewer = forwardRef(( + { + label, name, rules, ...props + }, + ref, +) => { + return ( + + + + + + + ); +}); + +CustomEditor.displayName = 'CustomEditor'; + const CustomForm = Object.assign(Form, { GridRow, Button: CustomButton, @@ -199,6 +234,8 @@ const CustomForm = Object.assign(Form, { Switch: CustomSwitch, Modal: CustomModal, useValidate, + Editor: CustomEditor, + Viewer: CustomViewer, }); export default CustomForm; diff --git a/src/components/common/CustomTable/index.tsx b/src/components/common/CustomTable/index.tsx index 57b2200..2933131 100644 --- a/src/components/common/CustomTable/index.tsx +++ b/src/components/common/CustomTable/index.tsx @@ -18,6 +18,12 @@ const TableContainer = styled.div` background-color: #e6f7ff; } } + .ant-table-cell { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 50px; + } .ant-pagination { margin: 20px 40px 0 0; align-self: flex-end; @@ -126,7 +132,7 @@ function CustomTable({ })} pagination={pagination ? false : { position: ['bottomRight'] }} /> - {pagination && ( + {pagination && pagination.total > 0 && ( ), getItem('혜택 관리', '/benefit', ), ]), - getItem('버스 정보', '/bus', ), getItem('복덕방', '/room', ), + getItem('공지사항', '/notice', ), ]), getItem('회원 관리', 'user', , [ @@ -76,6 +77,8 @@ function SideNav() { const onClick: MenuProps['onClick'] = (e) => { navigate(e.key); }; + const selectedKeys = pathname.startsWith('/notice') ? ['/notice'] : [pathname]; + return ( @@ -85,7 +88,7 @@ function SideNav() { = { shop_name: '상점명', shop_id: '상점 ID', image_url: '이미지', + post_number: '글번호', + title: '제목', + author: '작성자', + post_date: '게시일', }; diff --git a/src/model/notice.model.ts b/src/model/notice.model.ts new file mode 100644 index 0000000..dbe923c --- /dev/null +++ b/src/model/notice.model.ts @@ -0,0 +1,46 @@ +import { ListPagination } from './common.model'; + +export interface NoticesResponse extends ListPagination { + notices: Notice[]; +} + +export interface TransformedNoticesResponse { + notices: TransformedNotice[]; + total_page: number; +} + +export interface NoticesParam { + page: number; + is_deleted: boolean; +} + +export interface Notice { + id: number; + title: string; + author: string; + created_at: string; + updated_at: string; +} + +export interface NoticeResponse extends Notice { + content: string; +} + +export interface TransformedNotice { + id: number; + post_number: number; + title: string; + author: string; + post_date: string; +} + +export interface NoticeRequest { + title: string; + content: string; +} + +export interface NoticeUpdateForm { + id: number; + title: string; + content: string; +} diff --git a/src/model/upload.model.ts b/src/model/upload.model.ts index 1561bb4..69547d0 100644 --- a/src/model/upload.model.ts +++ b/src/model/upload.model.ts @@ -1,4 +1,4 @@ -export type Domain = 'items' | 'lands' | 'circles' | 'market' | 'shops' | 'members' | 'owners'; +export type Domain = 'items' | 'lands' | 'circles' | 'market' | 'shops' | 'members' | 'owners' | 'admin'; export interface Uploads { domain: Domain; diff --git a/src/pages/Services/Notice/NoticeDetail.style.tsx b/src/pages/Services/Notice/NoticeDetail.style.tsx new file mode 100644 index 0000000..b2b3005 --- /dev/null +++ b/src/pages/Services/Notice/NoticeDetail.style.tsx @@ -0,0 +1,28 @@ +import { styled } from 'styled-components'; + +export * from 'styles/Detail.style'; + +export const HeadingWrapper = styled.div` + display: flex; + align-items: center; + margin: 8px 0 16px; + .ant-btn-icon-only { + border: none; + margin-right: 5px; + } +`; + +export const ButtonWrapper = styled.div` + display: flex; + justify-content: flex-end; + width: calc(100% - 110px); +`; + +export const FormWrapper = styled.div` + padding: 10px 112px 60px 20px; + .ant-divider-with-text { + font-size: 15px; + font-weight: 600; + color: #2c3e50; + } +`; diff --git a/src/pages/Services/Notice/NoticeDetail.tsx b/src/pages/Services/Notice/NoticeDetail.tsx new file mode 100644 index 0000000..b4ae0e3 --- /dev/null +++ b/src/pages/Services/Notice/NoticeDetail.tsx @@ -0,0 +1,145 @@ +import '@toast-ui/editor/dist/toastui-editor.css'; +import '@toast-ui/editor/dist/i18n/ko-kr'; +import { useRef, useState } from 'react'; +import CustomForm from 'components/common/CustomForm'; +import { useNavigate, useParams } from 'react-router-dom'; +import { + DeleteOutlined, UploadOutlined, EditOutlined, LeftOutlined, CloseOutlined, +} from '@ant-design/icons'; +import { useGetNoticeQuery } from 'store/api/notice'; +import { + Button, Divider, Modal, message, +} from 'antd'; +import { Editor } from '@toast-ui/react-editor'; +import { useUploadfileMutation } from 'store/api/upload'; +import { NoticeRequest, NoticeUpdateForm } from 'model/notice.model'; +import useNoticeMutation from './useNoticeMutation'; +import * as S from './NoticeDetail.style'; + +export default function NoticeDetail() { + const navigate = useNavigate(); + const { id } = useParams(); + const [isEditing, setIsEditing] = useState(false); + const [isModalOpen, setIsModalOpen] = useState(false); + const editorRef = useRef(null); + const { data: notice } = useGetNoticeQuery(Number(id)); + const { updateNotice, deleteNotice } = useNoticeMutation(); + const [uploadfile] = useUploadfileMutation(); + const { required } = CustomForm.useValidate(); + const [form] = CustomForm.useForm(); + + const handleFinish = (values: NoticeRequest) => { + const editorContent = editorRef.current?.getInstance().getHTML(); + if (editorContent) values.content = editorContent; + + const noticeForm: NoticeUpdateForm = { + id: Number(id), + ...values, + }; + + updateNotice(noticeForm, { + onSuccess: () => setIsEditing(false), + }); + }; + + const handleImageUpload = async (blob: Blob) => { + const formData = new FormData(); + formData.append('multipartFile', blob); + return formData; + }; + + return ( + + {notice && ( + <> + + + + + + + )} + + ); +} diff --git a/src/pages/Services/Notice/NoticeList.style.tsx b/src/pages/Services/Notice/NoticeList.style.tsx new file mode 100644 index 0000000..cb78efb --- /dev/null +++ b/src/pages/Services/Notice/NoticeList.style.tsx @@ -0,0 +1,12 @@ +import { styled } from 'styled-components'; + +export * from 'styles/List.style'; + +export const ActionBar = styled.div` + margin-top: 24px; + width: 100%; + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; +`; diff --git a/src/pages/Services/Notice/NoticeList.tsx b/src/pages/Services/Notice/NoticeList.tsx new file mode 100644 index 0000000..2465e22 --- /dev/null +++ b/src/pages/Services/Notice/NoticeList.tsx @@ -0,0 +1,51 @@ +import { useNavigate } from 'react-router-dom'; +import CustomTable from 'components/common/CustomTable'; +import { useState } from 'react'; +import { Button, Switch } from 'antd'; +import { PlusOutlined } from '@ant-design/icons'; +import { useGetNoticeListQuery } from 'store/api/notice'; +import * as S from './NoticeList.style'; + +export default function NoticeList() { + const navigate = useNavigate(); + const [page, setPage] = useState(1); + const [showDeleted, setShowDeleted] = useState(false); + const { data: noticesResponse } = useGetNoticeListQuery({ + page, + is_deleted: showDeleted, + }); + + return ( + + 공지사항 목록 + + + + + + + {noticesResponse && ( + + )} + + ); +} diff --git a/src/pages/Services/Notice/NoticeWrite.style.tsx b/src/pages/Services/Notice/NoticeWrite.style.tsx new file mode 100644 index 0000000..32fb604 --- /dev/null +++ b/src/pages/Services/Notice/NoticeWrite.style.tsx @@ -0,0 +1,33 @@ +import styled from 'styled-components'; + +export * from 'styles/List.style'; + +export const Title = styled.div` + font-size: 24px; + font-weight: bold; +`; + +export const FormWrapper = styled.div` + padding: 10px 112px 20px 20px; + .ant-divider-with-text { + font-size: 15px; + font-weight: 600; + color: #2c3e50; + } +`; + +export const ButtonWrapper = styled.div` + display: flex; + justify-content: flex-end; + width: calc(100% - 110px); +`; + +export const HeadingWrapper = styled.div` + display: flex; + align-items: center; + margin: 8px 0 16px; + .ant-btn-icon-only { + border: none; + margin-right: 5px; + } +`; diff --git a/src/pages/Services/Notice/NoticeWrite.tsx b/src/pages/Services/Notice/NoticeWrite.tsx new file mode 100644 index 0000000..a1ea6cb --- /dev/null +++ b/src/pages/Services/Notice/NoticeWrite.tsx @@ -0,0 +1,86 @@ +/* eslint-disable no-restricted-imports */ +import '@toast-ui/editor/dist/toastui-editor.css'; +import '@toast-ui/editor/dist/i18n/ko-kr'; +import CustomForm from 'components/common/CustomForm'; +import { Button, message } from 'antd'; +import { useRef } from 'react'; +import { Editor } from '@toast-ui/react-editor'; +import { LeftOutlined, UploadOutlined } from '@ant-design/icons'; +import { useNavigate } from 'react-router-dom'; +import useNoticeMutation from 'pages/Services/Notice/useNoticeMutation'; +import { useUploadfileMutation } from 'store/api/upload'; +import * as S from './NoticeWrite.style'; + +export default function NoticeWrite() { + const navigate = useNavigate(); + const { required } = CustomForm.useValidate(); + const [form] = CustomForm.useForm(); + const editorRef = useRef(null); + const { addNotice } = useNoticeMutation(); + const [uploadfile] = useUploadfileMutation(); + + const handleFinish = (values: any) => { + const editorContent = editorRef.current?.getInstance().getHTML(); + if (editorContent) { + values.content = editorContent; + } + addNotice(values); + }; + + const handleImageUpload = async (blob: Blob) => { + const formData = new FormData(); + formData.append('multipartFile', blob); + return formData; + }; + + return ( + + +