From 41c8fea5946032d03047bfd923668cedc3a9125a Mon Sep 17 00:00:00 2001 From: leeyongjae Date: Tue, 15 Oct 2024 17:12:49 +0900 Subject: [PATCH] =?UTF-8?q?[QCAST]=20=EB=A1=9C=EA=B7=B8=EC=9D=B8,=20?= =?UTF-8?q?=EA=B0=80=EC=9E=85=EC=8B=A0=EC=B2=AD=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=EB=B0=8F=20=EB=82=B4=EC=A0=95=EB=B3=B4=ED=8C=9D=EC=97=85,=20?= =?UTF-8?q?=EC=BB=A4=EB=AE=A4=EB=8B=88=ED=8B=B0(=EA=B3=B5=EC=A7=80?= =?UTF-8?q?=EC=82=AC=ED=95=AD,=20FAQ,=20=EC=9E=90=EB=A3=8C=EB=8B=A4?= =?UTF-8?q?=EC=9A=B4=EB=A1=9C=EB=93=9C)=20=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 + src/app/community/archive/page.jsx | 5 +- src/app/community/faq/page.jsx | 6 +- src/app/community/notice/page.jsx | 6 +- src/app/join/complete/page.jsx | 16 +- src/app/layout.js | 21 +- src/app/login/page.jsx | 3 +- src/components/auth/Join.jsx | 526 +++++++++--------- src/components/auth/JoinComplete.jsx | 44 ++ src/components/auth/Login.jsx | 404 ++++++++------ src/components/auth/NewLogin.jsx | 244 -------- src/components/community/Archive.jsx | 66 ++- src/components/community/ArchiveTable.jsx | 103 ++++ src/components/community/Faq.jsx | 75 ++- src/components/community/Notice.jsx | 67 ++- src/components/community/Pagination.jsx | 78 +++ src/components/community/Search.jsx | 117 ++++ src/components/community/Table.jsx | 110 ++++ .../community/modal/BoardDetailModal.jsx | 77 +++ src/components/header/Header.jsx | 12 +- src/components/myInfo/UserInfoModal.jsx | 250 +++++++++ src/locales/ja.json | 121 ++-- src/locales/ko.json | 59 +- src/store/boardAtom.js | 14 + src/util/board-utils.js | 46 ++ yarn.lock | 5 + 26 files changed, 1683 insertions(+), 793 deletions(-) create mode 100644 src/components/auth/JoinComplete.jsx delete mode 100644 src/components/auth/NewLogin.jsx create mode 100644 src/components/community/ArchiveTable.jsx create mode 100644 src/components/community/Pagination.jsx create mode 100644 src/components/community/Search.jsx create mode 100644 src/components/community/Table.jsx create mode 100644 src/components/community/modal/BoardDetailModal.jsx create mode 100644 src/components/myInfo/UserInfoModal.jsx create mode 100644 src/store/boardAtom.js create mode 100644 src/util/board-utils.js diff --git a/package.json b/package.json index 260bfe8e..b44c2dda 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "framer-motion": "^11.2.13", "fs": "^0.0.1-security", "iron-session": "^8.0.2", + "js-cookie": "^3.0.5", "mathjs": "^13.0.2", "mssql": "^11.0.1", "next": "14.2.3", diff --git a/src/app/community/archive/page.jsx b/src/app/community/archive/page.jsx index 6917f228..308e02f3 100644 --- a/src/app/community/archive/page.jsx +++ b/src/app/community/archive/page.jsx @@ -7,10 +7,7 @@ export default async function CommunityArchivePage() { return ( <> - -
- -
+ ) } diff --git a/src/app/community/faq/page.jsx b/src/app/community/faq/page.jsx index 2b9d5452..054f9007 100644 --- a/src/app/community/faq/page.jsx +++ b/src/app/community/faq/page.jsx @@ -1,4 +1,3 @@ -import Hero from '@/components/Hero' import Faq from '@/components/community/Faq' import { initCheck } from '@/util/session-util' @@ -7,10 +6,7 @@ export default async function CommunityFaqPage() { return ( <> - -
- -
+ ) } diff --git a/src/app/community/notice/page.jsx b/src/app/community/notice/page.jsx index d2157b20..a3453e64 100644 --- a/src/app/community/notice/page.jsx +++ b/src/app/community/notice/page.jsx @@ -1,4 +1,3 @@ -import Hero from '@/components/Hero' import Notice from '@/components/community/Notice' import { initCheck } from '@/util/session-util' @@ -7,10 +6,7 @@ export default async function CommunityNoticePage() { return ( <> - -
- -
+ ) } diff --git a/src/app/join/complete/page.jsx b/src/app/join/complete/page.jsx index 3f9fc462..3f134e58 100644 --- a/src/app/join/complete/page.jsx +++ b/src/app/join/complete/page.jsx @@ -1,19 +1,9 @@ -'use client' - -import { useMessage } from '@/hooks/useMessage' - -export default function CompletePage() { - const { getMessage } = useMessage() +import JoinComplete from '@/components/auth/JoinComplete' +export default function JoinCompletePage() { return ( <> -
-

{getMessage('join.complete.title')}

-
{getMessage('join.complete.contents')}
-
- {getMessage('join.complete.email_comment')} : {getMessage('join.complete.email')} -
-
+ ) } diff --git a/src/app/layout.js b/src/app/layout.js index 6c04bbec..28626654 100644 --- a/src/app/layout.js +++ b/src/app/layout.js @@ -4,7 +4,6 @@ import { headers } from 'next/headers' import { redirect } from 'next/navigation' import { getSession } from '@/lib/authActions' import RecoilRootWrapper from './RecoilWrapper' -import UIProvider from './UIProvider' import { ToastContainer } from 'react-toastify' @@ -14,6 +13,7 @@ import QModal from '@/components/common/modal/QModal' import './globals.css' import '../styles/style.scss' +import '../styles/contents.scss' import Dimmed from '@/components/ui/Dimmed' // const inter = Inter({ subsets: ['latin'] }) @@ -61,18 +61,21 @@ export default async function RootLayout({ children }) { - {headerPathname !== '/login' ? ( + {headerPathname === '/login' || headerPathname === '/join' ? ( + {children} + ) : (
- -
- - {children} +
+ + {children} +
+
+
+ COPYRIGHT©2024 Hanwha Japan All Rights Reserved.
- +
- ) : ( - {children} )} diff --git a/src/app/login/page.jsx b/src/app/login/page.jsx index 3ff0edd7..0686da2e 100644 --- a/src/app/login/page.jsx +++ b/src/app/login/page.jsx @@ -1,10 +1,9 @@ import Login from '@/components/auth/Login' -import NewLogin from '@/components/auth/NewLogin' export default function LoginPage() { return ( <> - + ) } diff --git a/src/components/auth/Join.jsx b/src/components/auth/Join.jsx index ae100e68..5ae0b5ae 100644 --- a/src/components/auth/Join.jsx +++ b/src/components/auth/Join.jsx @@ -1,30 +1,27 @@ 'use client' import { useAxios } from '@/hooks/useAxios' -import { redirect } from 'next/navigation' +import { useRouter } from 'next/navigation' import { useMessage } from '@/hooks/useMessage' +import Cookies from 'js-cookie' export default function Join() { const { getMessage } = useMessage() const { post } = useAxios() + const router = useRouter() + // 가입 신청 const joinProcess = async (formData) => { + formData.preventDefault() + const param = { - langCd: 'JA', - lastEditUser: formData.get('userId'), storeQcastNm: formData.get('storeQcastNm'), storeQcastNmKana: formData.get('storeQcastNmKana'), postCd: formData.get('postCd'), addr: formData.get('addr'), telNo: formData.get('telNo'), fax: formData.get('fax'), - payTermsCd: 'JB02', - kamId: 'E1101011', - qtCompNm: formData.get('qtCompNm'), - qtPostCd: formData.get('qtPostCd'), - qtAddr: formData.get('qtAddr'), - qtTelNo: formData.get('qtTelNo'), - qtFax: formData.get('qtFax'), + bizNo: formData.get('bizNo'), userInfo: { userId: formData.get('userId'), userNm: formData.get('userNm'), @@ -39,7 +36,8 @@ export default function Join() { await post({ url: '/api/login/v1.0/user/join', data: param }).then((res) => { if (res) { if (res.result.resultCode == 'S') { - redirect('/join/complete') + Cookies.set('joinEmail', formData.get('email'), { expires: 1 }) + router.push('/join/complete') } else { alert(res.result.resultMsg) } @@ -48,275 +46,249 @@ export default function Join() { } return ( -
-

{getMessage('join.title')}

-
-
-
- ● {getMessage('join.sub1.title')} (*{getMessage('common.require')}) {getMessage('join.sub1.comment')} +
+
+ +
{getMessage('join.title')}
+
+
+
+

+ {getMessage('join.sub1.title')} (*{getMessage('common.require')}) +

+ {getMessage('join.sub1.comment')} +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ {getMessage('join.sub1.storeQcastNm')} * + +
+ +
+
+ {getMessage('join.sub1.storeQcastNmKana')} * + +
+ +
+
+ {getMessage('join.sub1.postCd')}/{getMessage('join.sub1.addr')} * + +
+
+ +
+
+ +
+
+
+ {getMessage('join.sub1.telNo')} * + +
+ +
+
+ {getMessage('join.sub1.fax')} * + +
+ +
+
{getMessage('join.sub1.bizNo')} +
+ +
+
+
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
{getMessage('join.sub1.storeQcastNm')} * - -
{getMessage('join.sub1.storeQcastNmKana')} * - -
- {getMessage('join.sub1.postCd')}/{getMessage('join.sub1.addr')} * - - - -
{getMessage('join.sub1.telNo')} * - -
{getMessage('join.sub1.fax')} * - -
- -
- ● {getMessage('join.sub2.title')} (*{getMessage('common.require')}) +
+
+
+

+ {getMessage('join.sub2.title')} (*{getMessage('common.require')}) +

+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ {getMessage('join.sub2.userNm')} * + +
+ +
+
{getMessage('join.sub2.userNmKana')} +
+ +
+
+ {getMessage('join.sub2.userId')} * + +
+ +
+
+ {getMessage('join.sub2.email')} * + +
+ +
+
+ {getMessage('join.sub2.telNo')} * + +
+ +
+
+ {getMessage('join.sub2.fax')} * + +
+ +
+
{getMessage('join.sub2.category')} +
+ +
+
+
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{getMessage('join.sub2.userNm')} * - -
{getMessage('join.sub2.userNmKana')} * - -
{getMessage('join.sub2.userId')} * - -
{getMessage('join.sub2.email')} * - -
{getMessage('join.sub2.telNo')} * - -
{getMessage('join.sub2.fax')} * - -
{getMessage('join.sub2.category')} - -
- -
- ● {getMessage('join.sub3.title')} (*{getMessage('common.require')}) +
+ +
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
{getMessage('join.sub3.qtCompNm')} - -
- {getMessage('join.sub3.qtPostCd')}/{getMessage('join.sub3.qtAddr')} - - - -
{getMessage('join.sub3.qtEmail')} - -
{getMessage('join.sub3.qtTelNo')} - -
{getMessage('join.sub3.qtFax')} - -
-
-
- -
- + +
) } diff --git a/src/components/auth/JoinComplete.jsx b/src/components/auth/JoinComplete.jsx new file mode 100644 index 00000000..c876f20a --- /dev/null +++ b/src/components/auth/JoinComplete.jsx @@ -0,0 +1,44 @@ +'use client' + +import { useMessage } from '@/hooks/useMessage' +import { useRouter } from 'next/navigation' +import { useState } from 'react' +import Cookies from 'js-cookie' + +export default function JoinComplete() { + const router = useRouter() + + const { getMessage } = useMessage() + const [emailText, setEmailText] = useState(Cookies.get('joinEmail')) + + return ( + <> +
+
+
+
+
{getMessage('join.complete.title')}
+
{getMessage('join.complete.contents')}
+
+
+ {getMessage('join.complete.email_comment')}: {emailText} +
+
+
+ +
+
+
+
+
+ + ) +} diff --git a/src/components/auth/Login.jsx b/src/components/auth/Login.jsx index ef9919c7..f6c810fb 100644 --- a/src/components/auth/Login.jsx +++ b/src/components/auth/Login.jsx @@ -1,62 +1,45 @@ 'use client' -import { useState } from 'react' +import { useState, useEffect } from 'react' +import Image from 'next/image' +import Link from 'next/link' +import { useRecoilState } from 'recoil' import { useAxios } from '@/hooks/useAxios' import { setSession } from '@/lib/authActions' -import { redirect } from 'next/navigation' import { useMessage } from '@/hooks/useMessage' - -import { Button, Switch } from '@nextui-org/react' -import { useRecoilState } from 'recoil' import { globalLocaleStore } from '@/store/localeAtom' -import { modalContent, modalState } from '@/store/modalAtom' import { sessionStore } from '@/store/commonAtom' +import { useRouter } from 'next/navigation' + +import Cookies from 'js-cookie' export default function Login() { - const { patch } = useAxios() + const [userId, setUserId] = useState('') + const [checkId, setCheckId] = useState('') + const [checkEmail, setCheckEmail] = useState('') + + useEffect(() => { + if (Cookies.get('chkLoginId')) { + setUserId(Cookies.get('chkLoginId')) + } + }, []) + + const [chkLoginId, setChkLoginId] = useState(Cookies.get('chkLoginId') ? true : false) + const [passwordVisible, setPasswordVisible] = useState(false) + const router = useRouter() const { getMessage } = useMessage() const [globalLocaleState, setGlbalLocaleState] = useRecoilState(globalLocaleStore) const [sessionState, setSessionState] = useRecoilState(sessionStore) - const [isSelected, setIsSelected] = useState(globalLocaleState === 'ko' ? true : false) - const handleSelected = () => { - if (isSelected) { - setGlbalLocaleState('ja') - } else { - setGlbalLocaleState('ko') - } + const [passwordReset, setPasswordReset] = useState(1) - setIsSelected(!isSelected) - } + const { post, patch } = useAxios(globalLocaleState) // login process - const loginProcess = async (formData) => { - const param = { - // langCd: currentLocale - langCd: globalLocaleState, - lastEditUser: formData.get('id'), - loginId: formData.get('id'), - pwd: formData.get('password'), - } - - // await post({ url: '/api/login/v1.0/login', data: param }).then((res) => { - // if (res) { - // if (res.result.resultCode == 'S') { - // // console.log('res.data', res.data) - // // 비밀번호 초기화가 필요한 경우 - // // if (res.data.pwdInitYn != 'Y') { - // // alert('비밀번호 초기화가 필요한 경우') - // // } else { - // setSession(res.data) - // redirect('/') - // // } - // } else { - // alert(res.result.resultMsg) - // } - // } - // }) - + const loginProcess = async (e) => { + e.preventDefault() + const formData = new FormData(e.target) // 임시 로그인 처리 setSession({ userId: 'NEW016610', @@ -71,7 +54,7 @@ export default function Login() { telNo: '336610', fax: null, email: 't10t@naver.com', - pwdInitYn: 'N', + pwdInitYn: 'Y', }) setSessionState({ @@ -87,155 +70,226 @@ export default function Login() { telNo: '336610', fax: null, email: 't10t@naver.com', - pwdInitYn: 'N', + pwdInitYn: 'Y', }) - redirect('/') + // ID SAVE 체크되어 있는 경우, 쿠키 저장 + if (chkLoginId) { + Cookies.set('chkLoginId', formData.get('id'), { expires: 7 }) + } else { + Cookies.remove('chkLoginId') + } + + router.push('/') // 임시 로그인 처리 끝 + + // 로그인 처리 시작 - ** 상단 임시 로그인 추후 삭제 필요 ** + // const param = { + // loginId: formData.get('id'), + // pwd: formData.get('password'), + // } + + // const res = await post({ url: '/api/login/v1.0/login', data: param }) + + // if (res) { + // if (res.result.resultCode == 'S') { + // setSession(res.data) + // setSessionState(res.data) + + // // ID SAVE 체크되어 있는 경우, 쿠키 저장 + // if (chkLoginId) { + // Cookies.set('chkLoginId', formData.get('id'), { expires: 7 }) + // } else { + // Cookies.remove('chkLoginId') + // } + + // router.push('/') + // } else { + // alert(res.result.resultMsg) + // } + // } } // 비밀번호 초기화 관련 - const [open, setOpen] = useRecoilState(modalState) - const [contents, setContent] = useRecoilState(modalContent) - - const initPasswordProcess = async (formData) => { + const initPasswordProcess = async () => { const param = { - langCd: currentLocale, - lastEditUser: formData.get('checkId'), - loginId: formData.get('checkId'), - email: formData.get('checkEmail'), + loginId: checkId, + email: checkEmail, } - await patch({ url: '/api/login/v1.0/user/init-password', data: param }).then((res) => { - if (res) { - if (res.result.resultCode == 'S') { - alert(getMessage('login.init_password.complete_message')) - redirect('/login') - } else { - alert(res.result.resultMsg) - } - } + const res = await patch({ + url: '/api/login/v1.0/user/init-password', + data: param, }) + + if (res) { + if (res.result.resultCode == 'S') { + alert(getMessage('login.init_password.complete_message')) + setCheckId('') + setCheckEmail('') + setPasswordReset(1) + } else { + alert(res.result.resultMsg) + } + } } - const initPasswordContent = ( -
-
-

{getMessage('login.init_password.title')}

-

{getMessage('login.init_password.sub_title')}

-
- -
- -
-
- -
-
- -
-
- -
-
-

- -

-
-
- ) - return ( -
-
-
-

{getMessage('site.name')}

-

{getMessage('site.sub_name')}

-
+
+
+ + react + -
-
-
- -
- + {passwordReset === 1 && ( + <> +
+ +
+ {getMessage('site.name')} + {getMessage('site.sub_name')} +
+
+
+ { + setUserId(e.target.value) + }} + /> + +
+
+ { + setPasswordVisible(passwordVisible) + }} + /> + +
+
+ { + setChkLoginId(e.target.checked) + }} + /> + +
+
+ +
+
+ +
+
+ +
+
+ + {getMessage('login.guide.text')} +
+ {getMessage('login.guide.sub1')} {getMessage('login.guide.join.btn')} + {getMessage('login.guide.sub2')} +
+ + )} + {passwordReset === 2 && ( + <> +
+
+ {getMessage('login.init_password.title')} + {getMessage('login.init_password.sub_title')} +
+
+
+ { + setCheckId(e.target.value) + }} + /> + +
+
+ { + setCheckEmail(e.target.value) + }} + placeholder={getMessage('login.init_password.email.placeholder')} + /> + +
+
+ + +
- -
-
- -
-
- -
-
- -
- -
- - -

- -

- -
- - {isSelected ? 'Current Locale: KO' : 'Current Locale: JA'} - -
-
+ + )}
+
COPYRIGHT©2024 Hanwha Japan All Rights Reserved.
) } diff --git a/src/components/auth/NewLogin.jsx b/src/components/auth/NewLogin.jsx deleted file mode 100644 index 61c02abf..00000000 --- a/src/components/auth/NewLogin.jsx +++ /dev/null @@ -1,244 +0,0 @@ -'use client' - -import { useState, useRef, useEffect } from 'react' -import Image from 'next/image' -import Link from 'next/link' -import { redirect } from 'next/navigation' -import { useRecoilState } from 'recoil' -import { useAxios } from '@/hooks/useAxios' -import { setSession } from '@/lib/authActions' -import { useMessage } from '@/hooks/useMessage' -import { globalLocaleStore } from '@/store/localeAtom' -import { sessionStore } from '@/store/commonAtom' -import { modalContent, modalState } from '@/store/modalAtom' -import '@/styles/style.scss' -import { useRouter } from 'next/navigation' - -export default function NewLogin() { - const [passwordVisible, setPasswordVisible] = useState(false) - const passwordRef = useRef(null) - const router = useRouter() - - useEffect(() => { - setOpen(false) - }, []) - - useEffect(() => { - if (passwordVisible) { - passwordRef.current.type = 'text' - } else { - passwordRef.current.type = 'password' - } - }, [passwordVisible]) - - const { patch } = useAxios() - - const { getMessage } = useMessage() - const [globalLocaleState, setGlbalLocaleState] = useRecoilState(globalLocaleStore) - const [sessionState, setSessionState] = useRecoilState(sessionStore) - const [isSelected, setIsSelected] = useState(globalLocaleState === 'ko' ? true : false) - - const handleSelected = () => { - if (isSelected) { - setGlbalLocaleState('ja') - } else { - setGlbalLocaleState('ko') - } - - setIsSelected(!isSelected) - } - - // login process - const loginProcess = async (formData) => { - const param = { - // langCd: currentLocale - langCd: globalLocaleState, - lastEditUser: formData.get('id'), - loginId: formData.get('id'), - pwd: formData.get('password'), - } - - // await post({ url: '/api/login/v1.0/login', data: param }).then((res) => { - // if (res) { - // if (res.result.resultCode == 'S') { - // // console.log('res.data', res.data) - // // 비밀번호 초기화가 필요한 경우 - // // if (res.data.pwdInitYn != 'Y') { - // // alert('비밀번호 초기화가 필요한 경우') - // // } else { - // setSession(res.data) - // redirect('/') - // // } - // } else { - // alert(res.result.resultMsg) - // } - // } - // }) - - // 임시 로그인 처리 - setSession({ - userId: 'NEW016610', - saleStoreId: null, - name: null, - mail: null, - tel: null, - storeId: 'TEMP02', - userNm: 'ㅇㅇ6610', - userNmKana: '신규사용자 16610', - category: '인상6610', - telNo: '336610', - fax: null, - email: 't10t@naver.com', - pwdInitYn: 'Y', - }) - - setSessionState({ - userId: 'NEW016610', - saleStoreId: null, - name: null, - mail: null, - tel: null, - storeId: 'TEMP02', - userNm: 'ㅇㅇ6610', - userNmKana: '신규사용자 16610', - category: '인상6610', - telNo: '336610', - fax: null, - email: 't10t@naver.com', - pwdInitYn: 'Y', - }) - - // redirect('/') - router.push('/') - // 임시 로그인 처리 끝 - } - - // 비밀번호 초기화 관련 - const [open, setOpen] = useRecoilState(modalState) - const [contents, setContent] = useRecoilState(modalContent) - - const initPasswordProcess = async (formData) => { - const param = { - langCd: currentLocale, - lastEditUser: formData.get('checkId'), - loginId: formData.get('checkId'), - email: formData.get('checkEmail'), - } - - await patch({ url: '/api/login/v1.0/user/init-password', data: param }).then((res) => { - if (res) { - if (res.result.resultCode == 'S') { - alert(getMessage('login.init_password.complete_message')) - redirect('/login') - } else { - alert(res.result.resultMsg) - } - } - }) - } - - const initPasswordContent = ( -
-
-

{getMessage('login.init_password.title')}

-

{getMessage('login.init_password.sub_title')}

-
- -
- -
-
- -
-
- -
-
- -
-
-

- -

-
-
- ) - - return ( -
-
- - react - -
-
-
- Q.CAST III - 太陽光発電システム図面管理サイト -
-
-
- - -
-
- - -
-
- - -
-
- -
-
- パスワードの初期化 -
-
-
-
-
- 当サイトをご利用の際は、事前申請が必要です。 -
- IDがない方は ID申請 クリックしてください。 -
-
-
COPYRIGHT©2024 Hanwha Japan All Rights Reserved.
-
- ) -} diff --git a/src/components/community/Archive.jsx b/src/components/community/Archive.jsx index dcfc737f..768bc500 100644 --- a/src/components/community/Archive.jsx +++ b/src/components/community/Archive.jsx @@ -1,7 +1,71 @@ +'use client' + +import Link from 'next/link' +import Image from 'next/image' + +import Search from '@/components/community/Search' +import ArchiveTable from '@/components/community/ArchiveTable' + +import { useEffect, useState } from 'react' +import { useResetRecoilState } from 'recoil' +import { useMessage } from '@/hooks/useMessage' + +import { searchState } from '@/store/boardAtom' + export default function Archive() { + const { getMessage } = useMessage() + const resetSearch = useResetRecoilState(searchState) + const [isInitialized, setIsInitialized] = useState(false) + + useEffect(() => { + resetSearch() + setIsInitialized(true) + }, []) + + if (!isInitialized) { + return null + } + + const boardType = { + boardTitle: getMessage('board.archive.title'), + subTitle: getMessage('board.archive.sub.title'), + clsCode: 'DOWN', + } + return ( <> -

Community Archive

+
+
+
    +
  • + + {getMessage('board.archive.title')} + +
  • +
+
    +
  • + + react + +
  • +
  • + {getMessage('header.menus.community')} +
  • +
  • + {getMessage('board.archive.title')} +
  • +
+
+
+
+
+
+ + +
+
+
) } diff --git a/src/components/community/ArchiveTable.jsx b/src/components/community/ArchiveTable.jsx new file mode 100644 index 00000000..318badef --- /dev/null +++ b/src/components/community/ArchiveTable.jsx @@ -0,0 +1,103 @@ +'use client' + +import { useEffect, useState } from 'react' +import { useRecoilState } from 'recoil' +import { useAxios } from '@/hooks/useAxios' + +import { searchState } from '@/store/boardAtom' +import { useMessage } from '@/hooks/useMessage' + +import { handleFileDown } from '@/util/board-utils' + +export default function ArchiveTable({ clsCode }) { + const { getMessage } = useMessage() + + // api 조회 관련 + const { get } = useAxios() + const [search, setSearch] = useRecoilState(searchState) + const [boardList, setBoardList] = useState([]) + + // 목록 조회 + useEffect(() => { + async function fetchData() { + const url = `${process.env.NEXT_PUBLIC_API_SERVER_PATH}/api/board/list` + const params = new URLSearchParams({ + schNoticeTpCd: 'QC', + schNoticeClsCd: clsCode, + schTitle: search.searchValue ? search.searchValue : '', + }) + const apiUrl = `${url}?${params.toString()}` + + const resultData = await get({ url: apiUrl }) + + if (resultData) { + if (resultData.result.code === 200) { + setBoardList(resultData.data) + if (resultData.data.length > 0) { + setSearch({ ...search, totalCount: resultData.data[0].totCnt }) + } else { + setSearch({ ...search, totalCount: 0 }) + } + } else { + alert(resultData.result.message) + } + } + } + + fetchData() + }, [search.searchValue]) + + // 상세 파일 목록 조회 + const handleDetailFileListDown = async (noticeNo) => { + const url = `${process.env.NEXT_PUBLIC_API_SERVER_PATH}/api/board/detail` + const params = new URLSearchParams({ + noticeNo: noticeNo, + }) + const apiUrl = `${url}?${params.toString()}` + + const resultData = await get({ url: apiUrl }) + + if (resultData) { + if (resultData.result.code === 200) { + const boardDetailFileList = resultData.data.listFile + + if (boardDetailFileList && Array.isArray(boardDetailFileList)) { + boardDetailFileList.forEach((boardFile) => { + handleFileDown(boardFile) + }) + } + } else { + alert(resultData.result.message) + } + } + } + + return ( + <> +
+ {boardList?.map((board) => ( +
+
+
+ {/* 번호 */} + {board.rowNumber} +
+
+ {/* 제목 */} + {board.title} +
+
+ {/* 등록일 */} + {getMessage('board.sub.updDt')} : {board.uptDt ? board.uptDt : board.regDt} +
+
+
+ {/* 첨부파일 */} + +
+
+ ))} +
+ + ) +} diff --git a/src/components/community/Faq.jsx b/src/components/community/Faq.jsx index 722741b3..17254f5c 100644 --- a/src/components/community/Faq.jsx +++ b/src/components/community/Faq.jsx @@ -1,7 +1,80 @@ +'use client' + +import Link from 'next/link' +import Image from 'next/image' + +import Search from '@/components/community/Search' +import Pagination from '@/components/community/Pagination' +import Table from '@/components/community/Table' + +import { useEffect, useState } from 'react' +import { useResetRecoilState, useRecoilValue, useRecoilState } from 'recoil' +import { useMessage } from '@/hooks/useMessage' + +import { searchState } from '@/store/boardAtom' + export default function Faq() { + const { getMessage } = useMessage() + const resetSearch = useResetRecoilState(searchState) + const [isInitialized, setIsInitialized] = useState(false) + + const search = useRecoilValue(searchState) + const [searchForm, setSearchForm] = useRecoilState(searchState) + + useEffect(() => { + if (search.mainFlag === 'N') { + resetSearch() + } else { + setSearchForm({ ...searchForm, mainFlag: 'N' }) + } + setIsInitialized(true) + }, []) + + if (!isInitialized) { + return null + } + + const boardType = { + boardTitle: getMessage('board.faq.title'), + subTitle: getMessage('board.faq.sub.title'), + clsCode: 'FAQ', + } + return ( <> -

Community FAQ

+
+
+
    +
  • + + {getMessage('board.faq.title')} + +
  • +
+
    +
  • + + react + +
  • +
  • + {getMessage('header.menus.community')} +
  • +
  • + {getMessage('board.faq.title')} +
  • +
+
+
+
+
+
+ + + + + + ) } diff --git a/src/components/community/Notice.jsx b/src/components/community/Notice.jsx index 2ab67c8e..55dfbad1 100644 --- a/src/components/community/Notice.jsx +++ b/src/components/community/Notice.jsx @@ -1,7 +1,72 @@ +'use client' + +import Link from 'next/link' +import Image from 'next/image' + +import Search from '@/components/community/Search' +import Pagination from '@/components/community/Pagination' +import Table from '@/components/community/Table' + +import { useEffect, useState } from 'react' +import { useResetRecoilState } from 'recoil' +import { useMessage } from '@/hooks/useMessage' + +import { searchState } from '@/store/boardAtom' + export default function Notice() { + const { getMessage } = useMessage() + const resetSearch = useResetRecoilState(searchState) + const [isInitialized, setIsInitialized] = useState(false) + + useEffect(() => { + resetSearch() + setIsInitialized(true) + }, []) + + if (!isInitialized) { + return null + } + + const boardType = { + boardTitle: getMessage('board.notice.title'), + subTitle: getMessage('board.notice.sub.title'), + clsCode: 'NOTICE', + } return ( <> -

Community Notice

+
+
+
    +
  • + + {getMessage('board.notice.title')} + +
  • +
+
    +
  • + + + +
  • +
  • + {getMessage('header.menus.community')} +
  • +
  • + {getMessage('board.notice.title')} +
  • +
+
+
+
+
+
+ +
+ + + + ) } diff --git a/src/components/community/Pagination.jsx b/src/components/community/Pagination.jsx new file mode 100644 index 00000000..938f5b0d --- /dev/null +++ b/src/components/community/Pagination.jsx @@ -0,0 +1,78 @@ +'use client' + +import { searchState } from '@/store/boardAtom' +import { useRecoilState, useRecoilValue } from 'recoil' +import { generateBlockPagination } from '@/util/board-utils' + +export default function Pagination() { + const search = useRecoilValue(searchState) + + const [searchForm, setSearchForm] = useRecoilState(searchState) + + const handlePagination = (pageNum) => { + setSearchForm({ ...searchForm, currentPage: pageNum }) + } + + const totalPages = Math.ceil(search.totalCount / search.pageBlock) > 0 ? Math.ceil(search.totalCount / search.pageBlock) : 1 + const allPages = generateBlockPagination(search.currentPage, totalPages, 10) + + return ( + <> +
    +
  1. + +
  2. +
  3. + +
  4. + + {/* 페이지목록 */} + {allPages.map((page, index) => { + return ( +
  5. + +
  6. + ) + })} + +
  7. + +
  8. +
  9. + +
  10. +
+ + ) +} diff --git a/src/components/community/Search.jsx b/src/components/community/Search.jsx new file mode 100644 index 00000000..26ca1883 --- /dev/null +++ b/src/components/community/Search.jsx @@ -0,0 +1,117 @@ +'use client' + +import { searchState } from '@/store/boardAtom' +import { useRecoilState, useRecoilValue } from 'recoil' +import { useState } from 'react' +import { useMessage } from '@/hooks/useMessage' + +export default function Search({ title = '', subTitle = '', isSelectUse = false }) { + const { getMessage } = useMessage() + + const search = useRecoilValue(searchState) + const [searchForm, setSearchForm] = useRecoilState(searchState) + + const [selectPageBlock, setSelectPageBlock] = useState(search.pageBlock) + const [searchValue, setSearchValue] = useState(search.searchValue) + const [searchView, setSearchView] = useState(search.searchValue ? true : false) + const [searchViewText, setSearchViewText] = useState(search.searchValue ? search.searchValue : '') + + // Enter 키 처리 + const handleKeyDown = (e) => { + if (e.key === 'Enter') { + handleSubmit(e) + } + } + + // 조회 값 처리 + const handleSearch = (text, block) => { + if (text !== '') { + setSearchView(true) + setSearchViewText(text) + setSearchForm({ ...searchForm, currentPage: 1, searchValue: text, pageBlock: block, searchFlag: true }) + } else { + setSearchView(false) + setSearchViewText('') + setSearchForm({ ...searchForm, currentPage: 1, searchValue: '', pageBlock: block, searchFlag: !searchForm.searchFlag }) + } + // 조회 후 값 비워주기 + setSearchValue('') + } + + const handleSubmit = (e) => { + e.preventDefault() + handleSearch(searchValue, selectPageBlock) + } + + return ( + <> +
+
+ { + setSearchValue(e.target.value) + }} + onKeyDown={handleKeyDown} + value={searchValue} + /> + +
+ {searchView && ( +
+ {isSelectUse ? ( + <> +
${searchViewText}`, `${search.totalCount}`]), + }} + >
+ + ) : ( + <> +
${searchViewText}`, `${search.totalCount}`]), + }} + >
+ + )} +
+ )} +
+
+
+

{subTitle}

+
    +
  • + {getMessage('board.sub.total')} {search.totalCount} +
  • +
+
+ {isSelectUse && ( +
+
+ +
+
+ )} +
+ + ) +} diff --git a/src/components/community/Table.jsx b/src/components/community/Table.jsx new file mode 100644 index 00000000..f943bc86 --- /dev/null +++ b/src/components/community/Table.jsx @@ -0,0 +1,110 @@ +'use client' + +import { useEffect, useState } from 'react' +import { useRecoilState } from 'recoil' + +import { searchState } from '@/store/boardAtom' + +import { useAxios } from '@/hooks/useAxios' +import { useMessage } from '@/hooks/useMessage' + +import BoardDetailModal from '../community/modal/BoardDetailModal' + +export default function Table({ clsCode }) { + const { getMessage } = useMessage() + + // api 조회 관련 + const { get } = useAxios() + const [search, setSearch] = useRecoilState(searchState) + const [boardList, setBoardList] = useState([]) + + // 팝업 관련 + const [open, setOpen] = useState(false) + const [modalNoticeNo, setModalNoticeNo] = useState('') + + // 목록 조회 + useEffect(() => { + async function fetchData() { + const startRow = (search.currentPage - 1) * search.pageBlock > 0 ? (search.currentPage - 1) * search.pageBlock + 1 : 1 + const endRow = search.currentPage * search.pageBlock + + const url = `/api/board/list` + const params = new URLSearchParams({ + schNoticeTpCd: 'QC', + schNoticeClsCd: clsCode, + schTitle: search.searchValue ? search.searchValue : '', + startRow: startRow, + endRow: endRow, + }) + const apiUrl = `${url}?${params.toString()}` + + const resultData = await get({ url: apiUrl }) + + if (resultData) { + if (resultData.result.code === 200) { + if (resultData.data.length > 0) { + setBoardList(resultData.data) + setSearch({ ...search, totalCount: resultData.data[0].totCnt }) + } else { + setBoardList([]) + setSearch({ ...search, totalCount: 0 }) + } + } else { + alert(resultData.result.message) + } + } + } + + fetchData() + }, [search.currentPage, search.searchValue, search.pageBlock, search.searchFlag]) + + return ( + <> +
+
+ + + + + + + {boardList.length > 0 ? ( + boardList?.map((board) => ( + { + setOpen(true) + setModalNoticeNo(board.noticeNo) + }} + > + + + + + )) + ) : ( + + + + )} + +
+ {/* 번호 */} + {board.rowNumber} + + {/* 제목 */} +
+
{board.title}
+ {board.attachYn && } +
+
+ {/* 등록일 */} + {board.regDt.split(' ')[0]} +
+ {getMessage('common.message.no.data')} +
+
+ {open && } + + ) +} diff --git a/src/components/community/modal/BoardDetailModal.jsx b/src/components/community/modal/BoardDetailModal.jsx new file mode 100644 index 00000000..dec5e09a --- /dev/null +++ b/src/components/community/modal/BoardDetailModal.jsx @@ -0,0 +1,77 @@ +'use client' + +import { useEffect, useState } from 'react' +import { useAxios } from '@/hooks/useAxios' +import { handleFileDown } from '@/util/board-utils' + +export default function BoardDetailModal({ noticeNo, setOpen }) { + // api 조회 관련 + const { get } = useAxios() + const [boardDetail, setBoardDetail] = useState({}) + + useEffect(() => { + // 상세 조회 + const fetchDetail = async (noticeNo) => { + const url = `/api/board/detail` + const params = new URLSearchParams({ + noticeNo: noticeNo, + }) + const apiUrl = `${url}?${params.toString()}` + + const resultData = await get({ url: apiUrl }) + + if (resultData) { + if (resultData.result.code === 200) { + const boardDetail = resultData.data + setBoardDetail(boardDetail) + } else { + alert(resultData.result.message) + } + } + } + + fetchDetail(noticeNo) + }, []) + + return ( + <> +
+
+
+
+ +
+
+
+
{boardDetail.title}
+ + {boardDetail.listFile && ( +
+
첨부파일 목록
+ {boardDetail.listFile.map((boardFile) => ( +
+ +
+ ))} +
+ )} + +
{boardDetail.contents}
+
+
+
+
+
+ + ) +} diff --git a/src/components/header/Header.jsx b/src/components/header/Header.jsx index 65da6051..c9b89658 100644 --- a/src/components/header/Header.jsx +++ b/src/components/header/Header.jsx @@ -12,6 +12,8 @@ import { logout } from '@/lib/authActions' import QSelectBox from '@/components/common/select/QSelectBox' +import UserInfoModal from '@/components/myInfo/UserInfoModal' + export const ToggleonMouse = (e, act, target) => { const listWrap = e.target.closest(target) const ListItem = Array.from(listWrap.childNodes) @@ -28,6 +30,8 @@ export const ToggleonMouse = (e, act, target) => { } export default function Header(props) { + const [userInfoModal, setUserInfoModal] = useState(false) + const { userSession } = props const [sessionState, setSessionState] = useRecoilState(sessionStore) const { getMessage } = useMessage() @@ -137,9 +141,15 @@ export default function Header(props) {
- + { + setUserInfoModal(true) + }} + > + {userInfoModal && }
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{getMessage('myinfo.info.userId')} +
+ +
+
{getMessage('myinfo.info.nameKana')} +
+ +
+
{getMessage('myinfo.info.name')} +
+ +
+
{getMessage('myinfo.info.password')} +
+
+ { + setPassword(e.target.value) + }} + /> + +
+ {getMessage('myinfo.sub.validation.password')} + +
+
+ {getMessage('myinfo.info.chg.password')} * + +
+
+ { + setChgPwd(e.target.value) + }} + /> +
+ + +
+
{getMessage('myinfo.info.category')} +
+ +
+
{getMessage('myinfo.info.tel')} +
+ +
+
{getMessage('myinfo.info.fax')} +
+ +
+
{getMessage('myinfo.info.mail')} +
+ +
+
+
+
+
+ +
+
+
+
+
+ + ) +} diff --git a/src/locales/ja.json b/src/locales/ja.json index 29ba0725..333ba900 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -322,53 +322,88 @@ "commons.south": "立つ", "commons.north": "北", "site.name": "Q.CAST III", - "site.sub_name": "태양광 발전 시스템 도면관리 사이트", - "login": "로그인", - "login.init_password.btn": "비밀번호 초기화 ja", - "login.init_password.title": "비밀번호 초기화", - "login.init_password.sub_title": "비밀번호를 초기화할 아이디와 이메일 주소를 입력해 주세요.", - "login.init_password.complete_message": "비밀번호가 초기화 되었습니다. 초기화된 비밀번호는 아이디와 같습니다.", - "join.title": "Q.CAST3 로그인ID 발행 신청", - "join.sub1.title": "판매대리점 정보", - "join.sub1.comment": "※ 등록되는 리셀러의 회사 이름을 입력하십시오. (2차점은 「○○판매주식회사(2차점:××설비주식회사)」로 기입해 주세요.)", - "join.sub1.storeQcastNm": "판매대리점명", - "join.sub1.storeQcastNm_placeholder": "株式会社エネルギア・ソリューション・アンド・サービス(2次店:山口住機販売有限会社)", - "join.sub1.storeQcastNmKana": "판매대리점명 후리가나", - "join.sub1.storeQcastNmKana_placeholder": "カブシキガイシャエネルギア・ソリューション・アン", - "join.sub1.postCd": "우편번호", - "join.sub1.postCd_placeholder": "숫자 7자리", - "join.sub1.addr": "주소", - "join.sub1.addr_placeholder": "전각50자이내", - "join.sub1.telNo": "전화번호", + "site.sub_name": "太陽光発電システム図面管理サイト", + "board.notice.title": "お知らせ", + "board.notice.sub.title": "お知らせ一覧", + "board.faq.title": "FAQ", + "board.faq.sub.title": "FAQ 一覧", + "board.archive.title": "資料ダウンロード", + "board.archive.sub.title": "文書一覧", + "board.list.header.rownum": "番号", + "board.list.header.title": "タイトル", + "board.list.header.regDt": "登録日", + "board.sub.search.placeholder": "検索語を入力してください。", + "board.sub.search.result": "{0}について、合計{1}件の投稿が検索されました。", + "board.sub.search.archive.result": "{0}について、合計{1}件の文書が検索されました。", + "board.sub.total": "全体", + "board.sub.fileList": "添付ファイル一覧", + "board.sub.updDt": "更新日", + "myinfo.title": "マイプロフィール", + "myinfo.info.userId": "ユーザーID", + "myinfo.info.nameKana": "担当者名ふりがな", + "myinfo.info.name": "担当者名", + "myinfo.info.password": "パスワード", + "myinfo.info.chg.password": "新しいパスワード入力", + "myinfo.info.category": "部署名", + "myinfo.info.tel": "電話番号", + "myinfo.info.fax": "FAX番号", + "myinfo.info.mail": "メールアドレス", + "myinfo.sub.validation.password": "※ 半角10文字以内", + "myinfo.btn.close": "閉じる", + "myinfo.btn.chg.password": "パスワード変更", + "myinfo.btn.chg": "変更", + "myinfo.btn.noChg": "変更しない", + "myinfo.btn.confirm": "確認", + "myinfo.message.validation.password1": "パスワードを入力してください。", + "myinfo.message.validation.password2": "既存のパスワードと同じです。", + "myinfo.message.validation.password3": "半角文字10文字以内で入力してください。", + "myinfo.message.save": "パスワードが変更されました。", + "login": "ログイン", + "login.id.save": "ID保存", + "login.id.placeholder": "IDを入力してください。", + "login.password.placeholder": "パスワードを入力してください。", + "login.guide.text": "当サイトを利用するには、事前申請が必要です。", + "login.guide.sub1": "IDをお持ちでない方は", + "login.guide.sub2": "をクリックしてください。", + "login.guide.join.btn": "ID申請", + "login.init_password.btn": "パスワードリセット", + "login.init_password.btn.back": "前の画面に戻る", + "login.init_password.title": "パスワードリセット", + "login.init_password.sub_title": "パスワードをリセットするIDとメールアドレスを入力してください。", + "login.init_password.complete_message": "パスワードがリセットされました。リセット後のパスワードはIDと同じです。", + "login.init_password.id.placeholder": "IDを入力してください。", + "login.init_password.email.placeholder": "メールアドレスを入力してください。", + "join.title": "Q.CAST3 ログインID発行申請", + "join.sub1.title": "販売代理店情報", + "join.sub1.comment": "※ 登録する販売店の会社名を入力してください。(2次店の場合「○○販売株式会社(2次店:××設備株式会社)」と記載してください。)", + "join.sub1.storeQcastNm": "販売代理店名", + "join.sub1.storeQcastNm_placeholder": "株式会社 エナジー ギア ソリューション アンド サービス(2次店: 山口重機販売有限会社)", + "join.sub1.storeQcastNmKana": "販売代理店名ふりがな", + "join.sub1.storeQcastNmKana_placeholder": "株式会社 エナジー ギア ソリューション", + "join.sub1.postCd": "郵便番号", + "join.sub1.postCd_placeholder": "7桁の数字", + "join.sub1.addr": "住所", + "join.sub1.addr_placeholder": "全角50文字以内", + "join.sub1.telNo": "電話番号", "join.sub1.telNo_placeholder": "00-0000-0000", - "join.sub1.fax": "FAX 번호", + "join.sub1.fax": "FAX番号", "join.sub1.fax_placeholder": "00-0000-0000", - "join.sub2.title": "담당자 정보", - "join.sub2.userNm": "담당자명", - "join.sub2.userNmKana": "담당자명 후리가나", - "join.sub2.userId": "신청 ID", - "join.sub2.email": "이메일 주소", - "join.sub2.telNo": "전화번호", + "join.sub1.bizNo": "法人番号", + "join.sub2.title": "担当者情報", + "join.sub2.userNm": "担当者名", + "join.sub2.userNmKana": "担当者名ふりがな", + "join.sub2.userId": "申請ID", + "join.sub2.email": "メールアドレス", + "join.sub2.telNo": "電話番号", "join.sub2.telNo_placeholder": "00-0000-0000", - "join.sub2.fax": "FAX 번호", + "join.sub2.fax": "FAX番号", "join.sub2.fax_placeholder": "00-0000-0000", - "join.sub2.category": "부서명", - "join.sub3.title": "견적서 제출용 회사정보", - "join.sub3.qtCompNm": "회사명", - "join.sub3.qtPostCd": "우편번호", - "join.sub3.qtPostCd_placeholder": "숫자 7자리", - "join.sub3.qtAddr": "주소", - "join.sub3.qtAddr_placeholder": "전각50자이내", - "join.sub3.qtEmail": "이메일 주소", - "join.sub3.qtTelNo": "전화번호", - "join.sub3.qtTelNo_placeholder": "00-0000-0000", - "join.sub3.qtFax": "FAX 번호", - "join.sub3.qtFax_placeholder": "00-0000-0000", - "join.btn.approval_request": "ID 승인요청", - "join.complete.title": "Q.CAST3 로그인ID 발행신청 완료", - "join.complete.contents": "※ 신청한 ID가 승인되면, 담당자 정보에 입력한 이메일 주소로 로그인 관련 안내 메일이 전송됩니다.", - "join.complete.email_comment": "담당자 이메일 주소", - "join.complete.email": "test@naver.com", + "join.sub2.category": "部署名", + "join.btn.login_page": "ログイン画面に移動", + "join.btn.approval_request": "ID承認申請", + "join.complete.title": "Q.CAST3 ログインID発行申請完了", + "join.complete.contents": "※ 申請したIDが承認されると、担当者情報に入力されたメールアドレスにログイン案内メールが送信されます。", + "join.complete.email_comment": "担当者メールアドレス", "stuff.gridHeader.lastEditDatetime": "更新日時", "stuff.gridHeader.objectNo": "品番", "stuff.gridHeader.planTotCnt": "プラン数", diff --git a/src/locales/ko.json b/src/locales/ko.json index 557c2a8c..a67eb769 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -327,11 +327,56 @@ "commons.north": "북", "site.name": "Q.CAST III", "site.sub_name": "태양광 발전 시스템 도면관리 사이트", + "board.notice.title": "공지사항", + "board.notice.sub.title": "공지사항 목록", + "board.faq.title": "FAQ", + "board.faq.sub.title": "FAQ 목록", + "board.archive.title": "자료 다운로드", + "board.archive.sub.title": "문서 목록", + "board.list.header.rownum": "번호", + "board.list.header.title": "제목", + "board.list.header.regDt": "등록일", + "board.sub.search.placeholder": "검색어를 입력하세요.", + "board.sub.search.result": "{0}에 대해 총 {1}건의 글이 검색 되었습니다.", + "board.sub.search.archive.result": "{0}에 대해 총 {1}건의 문서가 검색 되었습니다.", + "board.sub.total": "전체", + "board.sub.fileList": "첨부파일 목록", + "board.sub.updDt": "업데이트", + "myinfo.title": "My profile", + "myinfo.info.userId": "사용자ID", + "myinfo.info.nameKana": "담당자명 후리가나", + "myinfo.info.name": "담당자명", + "myinfo.info.password": "비밀번호", + "myinfo.info.chg.password": "변경 비밀번호 입력", + "myinfo.info.category": "부서명", + "myinfo.info.tel": "전화번호", + "myinfo.info.fax": "FAX번호", + "myinfo.info.mail": "이메일 주소", + "myinfo.sub.validation.password": "※ 반각10자 이내", + "myinfo.btn.close": "닫기", + "myinfo.btn.chg.password": "비밀번호 변경", + "myinfo.btn.chg": "변경", + "myinfo.btn.noChg": "변경안함", + "myinfo.btn.confirm": "확인", + "myinfo.message.validation.password1": "비밀번호를 입력하세요.", + "myinfo.message.validation.password2": "기존 비밀번호와 동일합니다.", + "myinfo.message.validation.password3": "반각 문자 10자이내여야 합니다.", + "myinfo.message.save": "비밀번호가 변경되었습니다.", "login": "로그인", + "login.id.save": "ID Save", + "login.id.placeholder": "아이디를 입력해주세요.", + "login.password.placeholder": "비밀번호를 입력해주세요.", + "login.guide.text": "당 사이트를 이용할 때는, 사전 신청이 필요합니다.", + "login.guide.sub1": "ID가 없는 분은", + "login.guide.sub2": "을 클릭해주십시오.", + "login.guide.join.btn": "ID신청", "login.init_password.btn": "비밀번호 초기화", + "login.init_password.btn.back": "이전 화면으로", "login.init_password.title": "비밀번호 초기화", "login.init_password.sub_title": "비밀번호를 초기화할 아이디와 이메일 주소를 입력해 주세요.", "login.init_password.complete_message": "비밀번호가 초기화 되었습니다. 초기화된 비밀번호는 아이디와 같습니다.", + "login.init_password.id.placeholder": "ID를 입력하세요.", + "login.init_password.email.placeholder": "이메일을 입력하세요.", "join.title": "Q.CAST3 로그인ID 발행 신청", "join.sub1.title": "판매대리점 정보", "join.sub1.comment": "※ 등록되는 리셀러의 회사 이름을 입력하십시오. (2차점은 「○○판매주식회사(2차점:××설비주식회사)」로 기입해 주세요.)", @@ -347,6 +392,7 @@ "join.sub1.telNo_placeholder": "00-0000-0000", "join.sub1.fax": "FAX 번호", "join.sub1.fax_placeholder": "00-0000-0000", + "join.sub1.bizNo": "법인번호", "join.sub2.title": "담당자 정보", "join.sub2.userNm": "담당자명", "join.sub2.userNmKana": "담당자명 후리가나", @@ -357,22 +403,11 @@ "join.sub2.fax": "FAX 번호", "join.sub2.fax_placeholder": "00-0000-0000", "join.sub2.category": "부서명", - "join.sub3.title": "견적서 제출용 회사정보", - "join.sub3.qtCompNm": "회사명", - "join.sub3.qtPostCd": "우편번호", - "join.sub3.qtPostCd_placeholder": "숫자 7자리", - "join.sub3.qtAddr": "주소", - "join.sub3.qtAddr_placeholder": "전각50자이내", - "join.sub3.qtEmail": "이메일 주소", - "join.sub3.qtTelNo": "전화번호", - "join.sub3.qtTelNo_placeholder": "00-0000-0000", - "join.sub3.qtFax": "FAX 번호", - "join.sub3.qtFax_placeholder": "00-0000-0000", + "join.btn.login_page": "로그인 화면으로 이동", "join.btn.approval_request": "ID 승인요청", "join.complete.title": "Q.CAST3 로그인ID 발행신청 완료", "join.complete.contents": "※ 신청한 ID가 승인되면, 담당자 정보에 입력한 이메일 주소로 로그인 관련 안내 메일이 전송됩니다.", "join.complete.email_comment": "담당자 이메일 주소", - "join.complete.email": "test@naver.com", "stuff.gridHeader.lastEditDatetime": "갱신일시", "stuff.gridHeader.objectNo": "물건번호", "stuff.gridHeader.planTotCnt": "플랜 수", diff --git a/src/store/boardAtom.js b/src/store/boardAtom.js new file mode 100644 index 00000000..3444eba0 --- /dev/null +++ b/src/store/boardAtom.js @@ -0,0 +1,14 @@ +import { atom } from 'recoil' + +export const searchState = atom({ + key: 'searchState', + default: { + currentPage: 1, + totalPage: 1, + pageBlock: 100, + totalCount: 0, + searchValue: '', + mainFlag: 'N', + searchFlag: false, + }, +}) diff --git a/src/util/board-utils.js b/src/util/board-utils.js new file mode 100644 index 00000000..515555dd --- /dev/null +++ b/src/util/board-utils.js @@ -0,0 +1,46 @@ +import { useAxios } from '@/hooks/useAxios' + +// 파일 다운로드 +export const handleFileDown = async (file) => { + const { get } = useAxios() + + const url = `/api/board/file/download` + const params = new URLSearchParams({ + encodeFileNo: file.encodeFileNo, + }) + const apiUrl = `${url}?${params.toString()}` + const resultData = await get({ url: apiUrl }) + + if (resultData) { + const blob = new Blob([resultData]) + const fileUrl = window.URL.createObjectURL(blob) + const link = document.createElement('a') + + link.href = fileUrl + link.download = file.srcFileNm + document.body.appendChild(link) + link.click() + link.remove() + window.URL.revokeObjectURL(url) + } +} + +// 페이지 번호 생성 +export const generateBlockPagination = (currentPage, totalPages, pageBlock) => { + const currentBlock = Math.ceil(currentPage / pageBlock) + + let startPage = (currentBlock - 1) * pageBlock + 1 + let endPage = startPage + pageBlock - 1 + + if (endPage > totalPages) { + endPage = totalPages + } + + let pageArr = [] + + for (let i = startPage; i <= endPage; i++) { + pageArr.push(i) + } + + return pageArr +} diff --git a/yarn.lock b/yarn.lock index 1ffd6b98..50679622 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5189,6 +5189,11 @@ jiti@^1.21.0: resolved "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz" integrity sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w== +js-cookie@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-3.0.5.tgz#0b7e2fd0c01552c58ba86e0841f94dc2557dcdbc" + integrity sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw== + js-md4@^0.3.2: version "0.3.2" resolved "https://registry.npmjs.org/js-md4/-/js-md4-0.3.2.tgz"