Merge branch 'dev' into dev-yj

This commit is contained in:
yjnoh 2024-09-11 12:34:54 +09:00
commit c726e2d871
24 changed files with 650 additions and 184 deletions

View File

@ -2,6 +2,7 @@ NEXT_PUBLIC_TEST="테스트변수입니다. development"
NEXT_PUBLIC_API_SERVER_PATH="http://1.248.227.176:38080" NEXT_PUBLIC_API_SERVER_PATH="http://1.248.227.176:38080"
# NEXT_PUBLIC_API_SERVER_PATH="http://localhost:8080" # NEXT_PUBLIC_API_SERVER_PATH="http://localhost:8080"
# NEXT_PUBLIC_API_SERVER_PATH="http://172.30.1.60:8080"
DATABASE_URL="sqlserver://mssql.devgrr.kr:1433;database=qcast;user=qcast;password=Qwertqaz12345;trustServerCertificate=true" DATABASE_URL="sqlserver://mssql.devgrr.kr:1433;database=qcast;user=qcast;password=Qwertqaz12345;trustServerCertificate=true"

Binary file not shown.

View File

@ -1,17 +1,17 @@
'use client' 'use client'
import { useI18n } from '@/locales/client' import { useMessage } from '@/hooks/useMessage'
export default function CompletePage() { export default function CompletePage() {
const t = useI18n() const { getMessage } = useMessage()
return ( return (
<> <>
<div className="flex min-h-full flex-1 flex-col justify-center px-6 py-12 lg:px-8"> <div className="flex min-h-full flex-1 flex-col justify-center px-6 py-12 lg:px-8">
<h1 className="text-center text-4xl font-bold mb-10">{t('join.complete.title')}</h1> <h1 className="text-center text-4xl font-bold mb-10">{getMessage('join.complete.title')}</h1>
<div className="mt-10 mb-10 w-full text-center text-2xl">{t('join.complete.contents')}</div> <div className="mt-10 mb-10 w-full text-center text-2xl">{getMessage('join.complete.contents')}</div>
<div className="mt-10 w-full text-center"> <div className="mt-10 w-full text-center">
{t('join.complete.email_comment')} :&nbsp;{t('join.complete.email')} {getMessage('join.complete.email_comment')} :&nbsp;{getMessage('join.complete.email')}
</div> </div>
</div> </div>
</> </>

View File

@ -1,13 +1,34 @@
'use client' 'use client'
import { useCurrentLocale } from '@/locales/client' import { useEffect } from 'react'
import { useRecoilState, useRecoilValue } from 'recoil'
import { appMessageStore, globalLocaleState } from '@/store/localeAtom'
import { LocaleProvider } from './LocaleProvider' import { LocaleProvider } from './LocaleProvider'
import { useCurrentLocale } from '@/locales/client'
import ServerError from './error' import ServerError from './error'
import { ErrorBoundary } from 'next/dist/client/components/error-boundary' import { ErrorBoundary } from 'next/dist/client/components/error-boundary'
import '@/styles/common.scss' import '@/styles/common.scss'
import KO from '@/locales/ko.json'
import JA from '@/locales/ja.json'
export default function LocaleLayout({ children }) { export default function LocaleLayout({ children }) {
const locale = useCurrentLocale() const locale = useCurrentLocale()
const globalLocale = useRecoilValue(globalLocaleState)
const [appMessageState, setAppMessageState] = useRecoilState(appMessageStore)
useEffect(() => {
console.log(globalLocale)
console.log(sessionStorage.getItem('hi'))
console.log(Object.keys(appMessageState).length)
if (Object.keys(appMessageState).length === 0) {
if (globalLocale === 'ko') {
setAppMessageState(KO)
} else {
setAppMessageState(JA)
}
}
}, [])
return ( return (
<> <>

View File

@ -119,7 +119,7 @@ export default function Playground() {
<div className="test"> <div className="test">
<p className="text-white">Sass 테스트입니다.</p> <p className="text-white">Sass 테스트입니다.</p>
</div> </div>
<div>{getMessage('hi')}</div> <div dangerouslySetInnerHTML={{ __html: getMessage('welcome', ['<span style="color: red">test</span>']) }}></div>
<div> <div>
<h1>React ColorPicker</h1> <h1>React ColorPicker</h1>
<ColorPicker color={color} setColor={setColor} /> <ColorPicker color={color} setColor={setColor} />

View File

@ -2,10 +2,10 @@
import { post } from '@/lib/Axios' import { post } from '@/lib/Axios'
import { redirect } from 'next/navigation' import { redirect } from 'next/navigation'
import { useI18n } from '@/locales/client' import { useMessage } from '@/hooks/useMessage'
export default function Join() { export default function Join() {
const t = useI18n() const { getMessage } = useMessage()
const joinProcess = async (formData) => { const joinProcess = async (formData) => {
const param = { const param = {
@ -48,11 +48,11 @@ export default function Join() {
return ( return (
<div className="flex min-h-full flex-1 flex-col justify-center px-6 py-12 lg:px-8"> <div className="flex min-h-full flex-1 flex-col justify-center px-6 py-12 lg:px-8">
<h1 className="text-center text-4xl font-bold">{t('join.title')}</h1> <h1 className="text-center text-4xl font-bold">{getMessage('join.title')}</h1>
<form action={joinProcess}> <form action={joinProcess}>
<div className="mt-10"> <div className="mt-10">
<div> <div>
{t('join.sub1.title')} (*{t('common.require')}) <span>{t('join.sub1.comment')}</span> {getMessage('join.sub1.title')} (*{getMessage('common.require')}) <span>{getMessage('join.sub1.comment')}</span>
</div> </div>
<table className="w-full"> <table className="w-full">
<colgroup> <colgroup>
@ -61,21 +61,21 @@ export default function Join() {
</colgroup> </colgroup>
<tbody> <tbody>
<tr> <tr>
<th style={{ border: '1px solid gray', padding: '5px' }}>{t('join.sub1.storeQcastNm')} *</th> <th style={{ border: '1px solid gray', padding: '5px' }}>{getMessage('join.sub1.storeQcastNm')} *</th>
<td style={{ border: '1px solid gray', padding: '5px' }}> <td style={{ border: '1px solid gray', padding: '5px' }}>
<input <input
type="text" type="text"
id="storeQcastNm" id="storeQcastNm"
name="storeQcastNm" name="storeQcastNm"
required required
alt={t('join.sub1.storeQcastNm')} alt={getMessage('join.sub1.storeQcastNm')}
className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300" className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300"
placeholder={t('join.sub1.storeQcastNm_placeholder')} placeholder={getMessage('join.sub1.storeQcastNm_placeholder')}
></input> ></input>
</td> </td>
</tr> </tr>
<tr> <tr>
<th style={{ border: '1px solid gray', padding: '5px' }}>{t('join.sub1.storeQcastNmKana')} *</th> <th style={{ border: '1px solid gray', padding: '5px' }}>{getMessage('join.sub1.storeQcastNmKana')} *</th>
<td style={{ border: '1px solid gray', padding: '5px' }}> <td style={{ border: '1px solid gray', padding: '5px' }}>
<input <input
type="text" type="text"
@ -83,13 +83,13 @@ export default function Join() {
name="storeQcastNmKana" name="storeQcastNmKana"
required required
className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300" className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300"
placeholder={t('join.sub1.storeQcastNmKana_placeholder')} placeholder={getMessage('join.sub1.storeQcastNmKana_placeholder')}
></input> ></input>
</td> </td>
</tr> </tr>
<tr> <tr>
<th style={{ border: '1px solid gray', padding: '5px' }}> <th style={{ border: '1px solid gray', padding: '5px' }}>
{t('join.sub1.postCd')}/{t('join.sub1.addr')} * {getMessage('join.sub1.postCd')}/{getMessage('join.sub1.addr')} *
</th> </th>
<td style={{ border: '1px solid gray', padding: '5px' }}> <td style={{ border: '1px solid gray', padding: '5px' }}>
<input <input
@ -98,7 +98,7 @@ export default function Join() {
name="postCd" name="postCd"
required required
className="block border-0 py-1.5 ring-1 ring-inset ring-gray-300" className="block border-0 py-1.5 ring-1 ring-inset ring-gray-300"
placeholder={t('join.sub1.postCd_placeholder')} placeholder={getMessage('join.sub1.postCd_placeholder')}
></input> ></input>
<input <input
type="text" type="text"
@ -106,12 +106,12 @@ export default function Join() {
name="addr" name="addr"
required required
className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300" className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300"
placeholder={t('join.sub1.addr_placeholder')} placeholder={getMessage('join.sub1.addr_placeholder')}
></input> ></input>
</td> </td>
</tr> </tr>
<tr> <tr>
<th style={{ border: '1px solid gray', padding: '5px' }}>{t('join.sub1.telNo')} *</th> <th style={{ border: '1px solid gray', padding: '5px' }}>{getMessage('join.sub1.telNo')} *</th>
<td style={{ border: '1px solid gray', padding: '5px' }}> <td style={{ border: '1px solid gray', padding: '5px' }}>
<input <input
type="text" type="text"
@ -119,12 +119,12 @@ export default function Join() {
name="telNo" name="telNo"
required required
className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300" className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300"
placeholder={t('join.sub1.telNo_placeholder')} placeholder={getMessage('join.sub1.telNo_placeholder')}
></input> ></input>
</td> </td>
</tr> </tr>
<tr> <tr>
<th style={{ border: '1px solid gray', padding: '5px' }}>{t('join.sub1.fax')} *</th> <th style={{ border: '1px solid gray', padding: '5px' }}>{getMessage('join.sub1.fax')} *</th>
<td style={{ border: '1px solid gray', padding: '5px' }}> <td style={{ border: '1px solid gray', padding: '5px' }}>
<input <input
type="text" type="text"
@ -132,7 +132,7 @@ export default function Join() {
name="fax" name="fax"
required required
className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300" className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300"
placeholder={t('join.sub1.fax_placeholder')} placeholder={getMessage('join.sub1.fax_placeholder')}
></input> ></input>
</td> </td>
</tr> </tr>
@ -140,7 +140,7 @@ export default function Join() {
</table> </table>
<div className="mt-5"> <div className="mt-5">
{t('join.sub2.title')} (*{t('common.require')}) {getMessage('join.sub2.title')} (*{getMessage('common.require')})
</div> </div>
<table className="w-full"> <table className="w-full">
<colgroup> <colgroup>
@ -149,7 +149,7 @@ export default function Join() {
</colgroup> </colgroup>
<tbody> <tbody>
<tr> <tr>
<th style={{ border: '1px solid gray', padding: '5px' }}>{t('join.sub2.userNm')} *</th> <th style={{ border: '1px solid gray', padding: '5px' }}>{getMessage('join.sub2.userNm')} *</th>
<td style={{ border: '1px solid gray', padding: '5px' }}> <td style={{ border: '1px solid gray', padding: '5px' }}>
<input <input
type="text" type="text"
@ -161,7 +161,7 @@ export default function Join() {
</td> </td>
</tr> </tr>
<tr> <tr>
<th style={{ border: '1px solid gray', padding: '5px' }}>{t('join.sub2.userNmKana')} *</th> <th style={{ border: '1px solid gray', padding: '5px' }}>{getMessage('join.sub2.userNmKana')} *</th>
<td style={{ border: '1px solid gray', padding: '5px' }}> <td style={{ border: '1px solid gray', padding: '5px' }}>
<input <input
type="text" type="text"
@ -173,7 +173,7 @@ export default function Join() {
</td> </td>
</tr> </tr>
<tr> <tr>
<th style={{ border: '1px solid gray', padding: '5px' }}>{t('join.sub2.userId')} *</th> <th style={{ border: '1px solid gray', padding: '5px' }}>{getMessage('join.sub2.userId')} *</th>
<td style={{ border: '1px solid gray', padding: '5px' }}> <td style={{ border: '1px solid gray', padding: '5px' }}>
<input <input
type="text" type="text"
@ -185,7 +185,7 @@ export default function Join() {
</td> </td>
</tr> </tr>
<tr> <tr>
<th style={{ border: '1px solid gray', padding: '5px' }}>{t('join.sub2.email')} *</th> <th style={{ border: '1px solid gray', padding: '5px' }}>{getMessage('join.sub2.email')} *</th>
<td style={{ border: '1px solid gray', padding: '5px' }}> <td style={{ border: '1px solid gray', padding: '5px' }}>
<input <input
type="email" type="email"
@ -198,7 +198,7 @@ export default function Join() {
</td> </td>
</tr> </tr>
<tr> <tr>
<th style={{ border: '1px solid gray', padding: '5px' }}>{t('join.sub2.telNo')} *</th> <th style={{ border: '1px solid gray', padding: '5px' }}>{getMessage('join.sub2.telNo')} *</th>
<td style={{ border: '1px solid gray', padding: '5px' }}> <td style={{ border: '1px solid gray', padding: '5px' }}>
<input <input
type="text" type="text"
@ -206,12 +206,12 @@ export default function Join() {
name="userTelNo" name="userTelNo"
required required
className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300" className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300"
placeholder={t('join.sub2.telNo_placeholder')} placeholder={getMessage('join.sub2.telNo_placeholder')}
></input> ></input>
</td> </td>
</tr> </tr>
<tr> <tr>
<th style={{ border: '1px solid gray', padding: '5px' }}>{t('join.sub2.fax')} *</th> <th style={{ border: '1px solid gray', padding: '5px' }}>{getMessage('join.sub2.fax')} *</th>
<td style={{ border: '1px solid gray', padding: '5px' }}> <td style={{ border: '1px solid gray', padding: '5px' }}>
<input <input
type="text" type="text"
@ -219,12 +219,12 @@ export default function Join() {
name="userFax" name="userFax"
required required
className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300" className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300"
placeholder={t('join.sub1.fax_placeholder')} placeholder={getMessage('join.sub1.fax_placeholder')}
></input> ></input>
</td> </td>
</tr> </tr>
<tr> <tr>
<th style={{ border: '1px solid gray', padding: '5px' }}>{t('join.sub2.category')}</th> <th style={{ border: '1px solid gray', padding: '5px' }}>{getMessage('join.sub2.category')}</th>
<td style={{ border: '1px solid gray', padding: '5px' }}> <td style={{ border: '1px solid gray', padding: '5px' }}>
<input id="category" name="category" className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300"></input> <input id="category" name="category" className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300"></input>
</td> </td>
@ -233,7 +233,7 @@ export default function Join() {
</table> </table>
<div className="mt-5"> <div className="mt-5">
{t('join.sub3.title')} (*{t('common.require')}) {getMessage('join.sub3.title')} (*{getMessage('common.require')})
</div> </div>
<table className="w-full"> <table className="w-full">
<colgroup> <colgroup>
@ -242,14 +242,14 @@ export default function Join() {
</colgroup> </colgroup>
<tbody> <tbody>
<tr> <tr>
<th style={{ border: '1px solid gray', padding: '5px' }}>{t('join.sub3.qtCompNm')}</th> <th style={{ border: '1px solid gray', padding: '5px' }}>{getMessage('join.sub3.qtCompNm')}</th>
<td style={{ border: '1px solid gray', padding: '5px' }}> <td style={{ border: '1px solid gray', padding: '5px' }}>
<input type="text" id="qtCompNm" name="qtCompNm" className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300"></input> <input type="text" id="qtCompNm" name="qtCompNm" className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300"></input>
</td> </td>
</tr> </tr>
<tr> <tr>
<th style={{ border: '1px solid gray', padding: '5px' }}> <th style={{ border: '1px solid gray', padding: '5px' }}>
{t('join.sub3.qtPostCd')}/{t('join.sub3.qtAddr')} {getMessage('join.sub3.qtPostCd')}/{getMessage('join.sub3.qtAddr')}
</th> </th>
<td style={{ border: '1px solid gray', padding: '5px' }}> <td style={{ border: '1px solid gray', padding: '5px' }}>
<input <input
@ -257,19 +257,19 @@ export default function Join() {
id="qtPostCd" id="qtPostCd"
name="qtPostCd" name="qtPostCd"
className="block border-0 py-1.5 ring-1 ring-inset ring-gray-300" className="block border-0 py-1.5 ring-1 ring-inset ring-gray-300"
placeholder={t('join.sub3.qtPostCd_placeholder')} placeholder={getMessage('join.sub3.qtPostCd_placeholder')}
></input> ></input>
<input <input
type="text" type="text"
id="qtAddr" id="qtAddr"
name="qtAddr" name="qtAddr"
className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300" className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300"
placeholder={t('join.sub3.qtAddr_placeholder')} placeholder={getMessage('join.sub3.qtAddr_placeholder')}
></input> ></input>
</td> </td>
</tr> </tr>
<tr> <tr>
<th style={{ border: '1px solid gray', padding: '5px' }}>{t('join.sub3.qtEmail')}</th> <th style={{ border: '1px solid gray', padding: '5px' }}>{getMessage('join.sub3.qtEmail')}</th>
<td style={{ border: '1px solid gray', padding: '5px' }}> <td style={{ border: '1px solid gray', padding: '5px' }}>
<input <input
type="email" type="email"
@ -281,26 +281,26 @@ export default function Join() {
</td> </td>
</tr> </tr>
<tr> <tr>
<th style={{ border: '1px solid gray', padding: '5px' }}>{t('join.sub3.qtTelNo')}</th> <th style={{ border: '1px solid gray', padding: '5px' }}>{getMessage('join.sub3.qtTelNo')}</th>
<td style={{ border: '1px solid gray', padding: '5px' }}> <td style={{ border: '1px solid gray', padding: '5px' }}>
<input <input
type="text" type="text"
id="qtTelNo" id="qtTelNo"
name="qtTelNo" name="qtTelNo"
className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300" className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300"
placeholder={t('join.sub3.qtTelNo_placeholder')} placeholder={getMessage('join.sub3.qtTelNo_placeholder')}
></input> ></input>
</td> </td>
</tr> </tr>
<tr> <tr>
<th style={{ border: '1px solid gray', padding: '5px' }}>{t('join.sub3.qtFax')}</th> <th style={{ border: '1px solid gray', padding: '5px' }}>{getMessage('join.sub3.qtFax')}</th>
<td style={{ border: '1px solid gray', padding: '5px' }}> <td style={{ border: '1px solid gray', padding: '5px' }}>
<input <input
type="text" type="text"
id="qtFax" id="qtFax"
name="qtFax" name="qtFax"
className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300" className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300"
placeholder={t('join.sub3.qtFax_placeholder')} placeholder={getMessage('join.sub3.qtFax_placeholder')}
></input> ></input>
</td> </td>
</tr> </tr>
@ -312,7 +312,7 @@ export default function Join() {
type="submit" type="submit"
className="w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600" className="w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
> >
{t('join.btn.approval_request')} {getMessage('join.btn.approval_request')}
</button> </button>
</div> </div>
</form> </form>

View File

@ -3,7 +3,7 @@
import { post, patch } from '@/lib/Axios' import { post, patch } from '@/lib/Axios'
import { setSession } from '@/lib/authActions' import { setSession } from '@/lib/authActions'
import { redirect } from 'next/navigation' import { redirect } from 'next/navigation'
import { useI18n } from '@/locales/client' import { useMessage } from '@/hooks/useMessage'
import { Button } from '@nextui-org/react' import { Button } from '@nextui-org/react'
import { useRecoilState } from 'recoil' import { useRecoilState } from 'recoil'
@ -11,7 +11,7 @@ import { modalContent, modalState } from '@/store/modalAtom'
export default function Login(props) { export default function Login(props) {
const { currentLocale } = props const { currentLocale } = props
const t = useI18n() const { getMessage } = useMessage()
// login process // login process
const loginProcess = async (formData) => { const loginProcess = async (formData) => {
@ -55,7 +55,7 @@ export default function Login(props) {
await patch({ url: '/api/login/v1.0/user/init-password', data: param }).then((res) => { await patch({ url: '/api/login/v1.0/user/init-password', data: param }).then((res) => {
if (res) { if (res) {
if (res.result.resultCode == 'S') { if (res.result.resultCode == 'S') {
alert(t('login.init_password.complete_message')) alert(getMessage('login.init_password.complete_message'))
redirect('/login') redirect('/login')
} else { } else {
alert(res.result.resultMsg) alert(res.result.resultMsg)
@ -67,8 +67,8 @@ export default function Login(props) {
const initPasswordContent = ( const initPasswordContent = (
<div className="flex min-h-full flex-1 flex-col justify-center px-6 py-12 lg:px-8"> <div className="flex min-h-full flex-1 flex-col justify-center px-6 py-12 lg:px-8">
<form action={initPasswordProcess} className="space-y-6"> <form action={initPasswordProcess} className="space-y-6">
<h2 className="text-center text-2xl font-bold leading-9 tracking-tight text-gray-900">{t('login.init_password.title')}</h2> <h2 className="text-center text-2xl font-bold leading-9 tracking-tight text-gray-900">{getMessage('login.init_password.title')}</h2>
<h2 className="text-center text-1xl font-bold leading-9 tracking-tight text-gray-900">{t('login.init_password.sub_title')}</h2> <h2 className="text-center text-1xl font-bold leading-9 tracking-tight text-gray-900">{getMessage('login.init_password.sub_title')}</h2>
<div> <div>
<label htmlFor="checkId" className="block text-sm font-medium leading-6 text-gray-900"> <label htmlFor="checkId" className="block text-sm font-medium leading-6 text-gray-900">
ID ID
@ -102,7 +102,7 @@ export default function Login(props) {
</div> </div>
<p className="mt-5 text-center text-sm text-gray-500"> <p className="mt-5 text-center text-sm text-gray-500">
<Button type="submit" className="font-semibold leading-6 text-indigo-600 hover:text-indigo-500"> <Button type="submit" className="font-semibold leading-6 text-indigo-600 hover:text-indigo-500">
{t('login.init_password.btn')} {getMessage('login.init_password.btn')}
</Button> </Button>
</p> </p>
</form> </form>
@ -113,8 +113,8 @@ export default function Login(props) {
<div className="flex flex-col align-center"> <div className="flex flex-col align-center">
<div className="flex min-h-full flex-1 flex-col justify-center px-6 py-12 lg:px-8"> <div className="flex min-h-full flex-1 flex-col justify-center px-6 py-12 lg:px-8">
<div className="mt-10 sm:mx-auto sm:w-full sm:max-w-sm"> <div className="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
<h1 className="text-center text-4xl font-bold leading-9 tracking-tight text-gray-900">{t('site.name')}</h1> <h1 className="text-center text-4xl font-bold leading-9 tracking-tight text-gray-900">{getMessage('site.name')}</h1>
<h2 className="mt-5 text-center text-2xl font-bold leading-9 tracking-tight text-gray-900">{t('site.sub_name')}</h2> <h2 className="mt-5 text-center text-2xl font-bold leading-9 tracking-tight text-gray-900">{getMessage('site.sub_name')}</h2>
</div> </div>
<div className="mt-5 sm:mx-auto sm:w-full sm:max-w-sm"> <div className="mt-5 sm:mx-auto sm:w-full sm:max-w-sm">
@ -158,7 +158,7 @@ export default function Login(props) {
type="submit" type="submit"
className="mt-10 flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600" className="mt-10 flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
> >
{t('login.login')} {getMessage('login')}
</button> </button>
</div> </div>
</form> </form>
@ -171,7 +171,7 @@ export default function Login(props) {
}} }}
className="font-semibold leading-6 text-indigo-600 hover:text-indigo-500" className="font-semibold leading-6 text-indigo-600 hover:text-indigo-500"
> >
{t('login.init_password.btn')} {getMessage('login.init_password.btn')}
</Button> </Button>
</p> </p>
</div> </div>

View File

@ -14,7 +14,7 @@ export default function QContextMenu(props) {
if (activeObject) { if (activeObject) {
if (activeObject.initOptions && activeObject.initOptions.name) { if (activeObject.initOptions && activeObject.initOptions.name) {
// //
if (activeObject.initOptions.name.indexOf('guide') > -1) { if (activeObject.initOptions?.name?.indexOf('guide') > -1) {
contextType = 'surface' // contextType = 'surface' //
} }
} }

View File

@ -1,7 +1,15 @@
export default function CanvasFrame () { import { useCanvas } from '@/hooks/useCanvas'
return( import { useRef } from 'react'
<div className="canvas-frame"> import { useEvent } from '@/hooks/useEvent'
<canvas></canvas>
</div> export default function CanvasFrame() {
) const canvasRef = useRef(null)
useCanvas('canvas')
useEvent()
return (
<div className="canvas-frame">
<canvas ref={canvasRef} id={'canvas'}></canvas>
</div>
)
} }

View File

@ -20,7 +20,7 @@ export const ToggleonMouse = (e, act, target) => {
export default function Header() { export default function Header() {
const pathName = usePathname() const pathName = usePathname()
if (pathName.includes('login')) { if (pathName.includes('login') || pathName.includes('join')) {
return null return null
} }
const SelectOption = [{ name: 'オンライン保証シ' }, { name: 'ステム' }] const SelectOption = [{ name: 'オンライン保証シ' }, { name: 'ステム' }]

View File

@ -4,21 +4,22 @@ import React, { useEffect, useState, useRef } from 'react'
import { useRouter, usePathname } from 'next/navigation' import { useRouter, usePathname } from 'next/navigation'
import { Button } from '@nextui-org/react' import { Button } from '@nextui-org/react'
import { useAxios } from '@/hooks/useAxios' import { useAxios } from '@/hooks/useAxios'
import { useMessage } from '@/hooks/useMessage'
import StuffQGrid from './StuffQGrid' import StuffQGrid from './StuffQGrid'
import { useI18n } from '@/locales/client'
import { useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
import { stuffSearchState } from '@/store/stuffAtom' import { stuffSearchState } from '@/store/stuffAtom'
import { queryStringFormatter } from '@/util/common-utils' import { queryStringFormatter, isEmptyArray } from '@/util/common-utils'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import isLeapYear from 'dayjs/plugin/isLeapYear' // import isLeapYear from 'dayjs/plugin/isLeapYear' //
dayjs.extend(isLeapYear) dayjs.extend(isLeapYear)
export default function Stuff() { export default function Stuff() {
const stuffSearchParams = useRecoilValue(stuffSearchState) const stuffSearchParams = useRecoilValue(stuffSearchState)
const { getMessage } = useMessage()
const [curPage, setCurPage] = useState(1) //
const [size, setSize] = useState(100) //
const { get, del } = useAxios() const { get, del } = useAxios()
const gridRef = useRef() const gridRef = useRef()
const lang = useI18n()
const [gridCount, setGridCount] = useState(0) const [gridCount, setGridCount] = useState(0)
const [selectedRowData, setSelectedRowData] = useState([]) const [selectedRowData, setSelectedRowData] = useState([])
@ -47,7 +48,7 @@ export default function Stuff() {
gridColumns: [ gridColumns: [
{ {
field: 'lastEditDatetime', field: 'lastEditDatetime',
headerName: lang('stuff.gridHeader.lastEditDatetime'), headerName: getMessage('stuff.gridHeader.lastEditDatetime'),
headerCheckboxSelection: true, headerCheckboxSelection: true,
headerCheckboxSelectionCurrentPageOnly: true, // headerCheckboxSelectionCurrentPageOnly: true, //
checkboxSelection: true, checkboxSelection: true,
@ -73,7 +74,7 @@ export default function Stuff() {
}, },
{ {
field: 'objectNo', field: 'objectNo',
headerName: lang('stuff.gridHeader.objectNo'), headerName: getMessage('stuff.gridHeader.objectNo'),
// headerClass: 'centered', //_test.scss // headerClass: 'centered', //_test.scss
cellRenderer: function (params) { cellRenderer: function (params) {
if (params.data.objectNo) { if (params.data.objectNo) {
@ -100,22 +101,22 @@ export default function Stuff() {
}, },
{ {
field: 'planTotCnt', field: 'planTotCnt',
headerName: lang('stuff.gridHeader.planTotCnt'), headerName: getMessage('stuff.gridHeader.planTotCnt'),
cellStyle: { textAlign: 'right' }, cellStyle: { textAlign: 'right' },
}, },
{ field: 'objectName', headerName: lang('stuff.gridHeader.objectName'), cellStyle: { textAlign: 'left' } }, { field: 'objectName', headerName: getMessage('stuff.gridHeader.objectName'), cellStyle: { textAlign: 'left' } },
{ {
field: 'saleStoreId', field: 'saleStoreId',
headerName: lang('stuff.gridHeader.saleStoreId'), headerName: getMessage('stuff.gridHeader.saleStoreId'),
cellStyle: { textAlign: 'left' }, cellStyle: { textAlign: 'left' },
}, },
{ field: 'saleStoreName', headerName: lang('stuff.gridHeader.saleStoreName'), cellStyle: { textAlign: 'left' } }, { field: 'saleStoreName', headerName: getMessage('stuff.gridHeader.saleStoreName'), cellStyle: { textAlign: 'left' } },
{ field: 'address', headerName: lang('stuff.gridHeader.address'), cellStyle: { textAlign: 'left' } }, { field: 'address', headerName: getMessage('stuff.gridHeader.address'), cellStyle: { textAlign: 'left' } },
{ field: 'dispCompanyName', headerName: lang('stuff.gridHeader.dispCompanyName'), cellStyle: { textAlign: 'left' } }, { field: 'dispCompanyName', headerName: getMessage('stuff.gridHeader.dispCompanyName'), cellStyle: { textAlign: 'left' } },
{ field: 'receiveUser', headerName: lang('stuff.gridHeader.receiveUser'), cellStyle: { textAlign: 'left' } }, { field: 'receiveUser', headerName: getMessage('stuff.gridHeader.receiveUser'), cellStyle: { textAlign: 'left' } },
{ {
field: 'specDate', field: 'specDate',
headerName: lang('stuff.gridHeader.specDate'), headerName: getMessage('stuff.gridHeader.specDate'),
valueFormatter: function (params) { valueFormatter: function (params) {
if (params.value) { if (params.value) {
return dayjs(params?.value).format('YYYY.MM.DD') return dayjs(params?.value).format('YYYY.MM.DD')
@ -127,7 +128,7 @@ export default function Stuff() {
}, },
{ {
field: 'createDatetime', field: 'createDatetime',
headerName: lang('stuff.gridHeader.createDatetime'), headerName: getMessage('stuff.gridHeader.createDatetime'),
valueFormatter: function (params) { valueFormatter: function (params) {
if (params.value) { if (params.value) {
return dayjs(params?.value).format('YYYY.MM.DD') return dayjs(params?.value).format('YYYY.MM.DD')
@ -264,18 +265,36 @@ export default function Stuff() {
schDateType: 'U', schDateType: 'U',
schFromDt: dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'), schFromDt: dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'),
schToDt: dayjs(new Date()).format('YYYY-MM-DD'), schToDt: dayjs(new Date()).format('YYYY-MM-DD'),
startRow: (curPage - 1) * size + 1,
endRow: curPage * size,
} }
async function fetchData() { async function fetchData() {
console.log('화면진입:::::::::::::', params) console.log('화면진입:::::::::::::', params)
const apiUrl = `/api/object/v1.0/object?saleStoreId=201TES01&${queryStringFormatter(params)}` console.log('현재페이지::::::', curPage)
// console.log('apiUrl::', apiUrl) console.log('페이지당 게시물수::::::', size)
//api startRow, endRow
// let startRow
// let endRow
// startRow = (curPage - 1) * size + 1
// endRow = curPage * size
// console.log('startrow::', startRow)
// console.log('endRow::', endRow)
// let curPage
// let totalpage
// let totalCount
// let size
// let pageCount
const apiUrl = `/api/object/list?saleStoreId=201TES01&${queryStringFormatter(params)}`
await get({ await get({
url: apiUrl, url: apiUrl,
}).then((res) => { }).then((res) => {
if (res.length > 0) { if (!isEmptyArray(res)) {
console.log('API결과:::::::', res) console.log('화면진입API결과::', res)
setGridProps({ ...gridProps, gridData: res, count: res.length }) setGridProps({ ...gridProps, gridData: res, count: res.length })
setGridCount(res.length) setGridCount(res.length)
} }
@ -289,11 +308,13 @@ export default function Stuff() {
if (stuffSearchParams?.code === 'E') { if (stuffSearchParams?.code === 'E') {
console.log('조회 눌럿을때 ::::::::::::::', stuffSearchParams) console.log('조회 눌럿을때 ::::::::::::::', stuffSearchParams)
async function fetchData() { async function fetchData() {
const apiUrl = `/api/object/v1.0/object?saleStoreId=201TES01&${queryStringFormatter(stuffSearchParams)}` const apiUrl = `/api/object/list?saleStoreId=201TES01&${queryStringFormatter(stuffSearchParams)}`
await get({ url: apiUrl }).then((res) => { await get({ url: apiUrl }).then((res) => {
console.log('API결과:::::::', res) console.log('API결과:::::::', res)
setGridProps({ ...gridProps, gridData: res, count: res.length }) if (!isEmptyArray(res)) {
setGridCount(res.length) setGridProps({ ...gridProps, gridData: res, count: res.length })
setGridCount(res.length)
}
}) })
} }
fetchData() fetchData()

View File

@ -2,14 +2,49 @@
import React, { useState, useEffect } from 'react' import React, { useState, useEffect } from 'react'
import { useRouter, useSearchParams } from 'next/navigation' import { useRouter, useSearchParams } from 'next/navigation'
import { Input, RadioGroup, Radio, Button, Autocomplete, AutocompleteItem, Select, SelectItem, Checkbox, Textarea } from '@nextui-org/react' import { Input, RadioGroup, Radio, Button, Autocomplete, AutocompleteItem, Select, SelectItem, Checkbox, Textarea, button } from '@nextui-org/react'
import Link from 'next/link' import Link from 'next/link'
import { get } from '@/lib/Axios' import { get } from '@/lib/Axios'
import { queryStringFormatter } from '@/util/common-utils' import { queryStringFormatter, isEmptyArray } from '@/util/common-utils'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { useForm } from 'react-hook-form'
export default function StuffDetail() { export default function StuffDetail() {
const router = useRouter() const router = useRouter()
const searchParams = useSearchParams() const searchParams = useSearchParams()
//form
const formInitValue = {
// T...() R...()
dispCompanyName: '', //
objectStatusId: '0', //(:0 : 1)
objectName: '', //
objectNameOmit: '', //
objectNameKana: '', //
saleStoreId: '', //ID
saleStoreName: '', //
zipNo: '', //
prefId: '', //
address: '', //
powerSimArea: '', //
windSpeed: '', //
snowCover: '', //
coldAreaChk: false, //
surfaceType: 'Ⅲ・Ⅳ', //( / )
saltAreaChk: false, //
installHeight: '', //
powerConTerms: '0', //( / )
remark: '', //
tempFlag: 'T', //(1) (0)
}
const { register, setValue, getValues, handleSubmit, resetField, control, watch } = useForm({
defaultValues: formInitValue,
})
const form = { register, setValue, getValues, handleSubmit, resetField, control, watch }
const [prefCodeList, setPrefCodeList] = useState([]) //
const [prefValue, setPrefValue] = useState('')
const [receiveUser, setReceiveUser] = useState('') // const [receiveUser, setReceiveUser] = useState('') //
const [name2, setName2] = useState('') // const [name2, setName2] = useState('') //
const [name3, setName3] = useState('') // const [name3, setName3] = useState('') //
@ -25,7 +60,7 @@ export default function StuffDetail() {
const [isFormValid, setIsFormValid] = useState(false) //, const [isFormValid, setIsFormValid] = useState(false) //,
const [testSelOption, setTestSelOption] = useState([]) // const [testSelOption, setTestSelOption] = useState([]) //
const [autoSelectValue, setAutoSelectValue] = useState('') // const [autoSelectValue, setAutoSelectValue] = useState('') //
const [buttonValid, setButtonValid] = useState(true) // const [buttonValid, setButtonValid] = useState(false) //
const [isSelected, setIsSelected] = useState(false) // const [isSelected, setIsSelected] = useState(false) //
const [isSelected2, setIsSelected2] = useState(false) // const [isSelected2, setIsSelected2] = useState(false) //
const [gubun2, setGubun2] = useState('1') // const [gubun2, setGubun2] = useState('1') //
@ -33,47 +68,51 @@ export default function StuffDetail() {
const [memo, setMemo] = useState('') // const [memo, setMemo] = useState('') //
const objectNo = searchParams.get('objectNo') //url set const objectNo = searchParams.get('objectNo') //url set
const [address1, setAddress1] = useState('') //API // const [address1, setAddress1] = useState('') //API
const [address2, setAddress2] = useState('') //API // const [address2, setAddress2] = useState('') //API
const [address3, setAddress3] = useState('') //API // const [address3, setAddress3] = useState('') //API
const [prefcode, setPrefCode] = useState(1) //API prefcode // const [prefCode, setPrefCode] = useState(1) //API prefcode
const [editMode, setEditMode] = useState('NEW') const [editMode, setEditMode] = useState('NEW')
const [detailData, setDetailData] = useState({}) const [detailData, setDetailData] = useState({})
useEffect(() => { useEffect(() => {
get({ url: '/api/object/prefecture/list' }).then((res) => {
if (!isEmptyArray(res)) {
console.log('도도부현API 결과:::', res)
setPrefCodeList(res)
}
})
// console.log(':::::::::', searchParams.get('objectNo')) // console.log(':::::::::', searchParams.get('objectNo'))
// console.log('::::', objectNo) // console.log('::::', objectNo)
if (objectNo) { if (objectNo) {
console.log('상세::')
setEditMode('EDIT') setEditMode('EDIT')
//http://localhost:8080/api/object/v1.0/object/R201TES01240906007/1 if (objectNo.substring(0, 1) === 'R') {
// 1 setIsFormValid(true)
//API }
get({ url: `/api/object/v1.0/object/${objectNo}/1` }).then((res) => { get({ url: `/api/object/${objectNo}/detail` }).then((res) => {
if (res != null) { if (res != null) {
// console.log('res:::::::', res) // console.log('res:::::::', res)
setDetailData(res) setDetailData(res)
//setTestSelOption(res)
} }
}) })
} else {
console.log('신규:::')
} }
}, [objectNo]) }, [objectNo])
useEffect(() => { useEffect(() => {
validateForm() // validateForm()
}, [receiveUser, name2, name3, gubun, sel, autoSelectValue, zipCode, sel2, sel3, name5, sel4]) }, [receiveUser, name2, name3, gubun, sel, autoSelectValue, zipCode, sel2, sel3, name5, sel4])
// //
const textTypeHandler = (e) => { const _zipNo = watch('zipNo')
//\D () , [^0-9] useEffect(() => {
if (!e.target.value.match(/\D/g)) { if (_zipNo !== '' && _zipNo.length === 7 && !_zipNo.match(/\D/g)) {
setZipCode(e.target.value) setButtonValid(true)
} else {
setButtonValid(false)
} }
} }, [_zipNo])
// //
const textTypeHandler2 = (e) => { const textTypeHandler2 = (e) => {
@ -127,30 +166,34 @@ export default function StuffDetail() {
errors.name5 = '수직적설량 is required.' errors.name5 = '수직적설량 is required.'
} }
// console.log('errors::', errors) console.log('errors::', errors)
setErrors(errors) setErrors(errors)
setIsFormValid(Object.keys(errors).length === 0) setIsFormValid(Object.keys(errors).length === 0)
} }
// API // API
const onSearchPostNumber = () => { const onSearchPostNumber = () => {
if (!zipCode) {
return alert('우편번호 입력해')
}
const params = { const params = {
zipcode: zipCode, zipcode: _zipNo,
} }
get({ url: `https://zipcloud.ibsnet.co.jp/api/search?${queryStringFormatter(params)}` }).then((res) => { get({ url: `https://zipcloud.ibsnet.co.jp/api/search?${queryStringFormatter(params)}` }).then((res) => {
console.log('우편API RES::::::::', res) //7830060
if (res.status === 200) { if (res.status === 200) {
if (res.results.length > 0) { console.log('res.results::', res.results)
setAddress1(res.results[0].address1) if (res.results != null) {
setAddress2(res.results[0].address2) console.log('res.results::', res.results)
setAddress3(res.results[0].address3) // prefId: '', //
setPrefCode(res.results[0].prefcode) // address: '', //
console.log('prefcode::', res.results[0].prefcode)
console.log('address::', res.results[0].address2 + res.results[0].address3)
setPrefValue(res.results[0].prefcode)
form.setValue('prefId', res.results[0].prefcode)
form.setValue('address', res.results[0].address2 + res.results[0].address3)
} else { } else {
alert('등록된 우편번호에서 주소를 찾을 수 없습니다. 다시 입력해주세요.') alert('등록된 우편번호에서 주소를 찾을 수 없습니다. 다시 입력해주세요.')
form.setValue('prefId', '')
form.setValue('address', '')
setPrefValue('')
} }
} else { } else {
alert(res.message) alert(res.message)
@ -174,10 +217,173 @@ export default function StuffDetail() {
console.log('e:::::::', e.target.value) console.log('e:::::::', e.target.value)
} }
//
const onValid = (data) => {
console.log('data::::::', data)
const formData = form.getValues()
//console.log('formData::::', formData)
const _dispCompanyName = watch('dispCompanyName')
const _objectStatusId = watch('objectStatusId')
const _objectNameOmit = watch('objectNameOmit')
const _zipNo = watch('zipNo')
const _prefId = watch('prefId')
const _address = watch('address')
const _coldAreaChk = watch('coldAreaChk')
console.log(_dispCompanyName)
console.log(_objectStatusId)
console.log(_objectNameOmit)
console.log(_zipNo)
console.log(_prefId)
console.log('prefValue::', prefValue)
console.log(_address)
console.log('_coldAreaChk::', _coldAreaChk)
}
//
const onInvalid = (errors) => {
console.log('실패', errors)
}
return ( return (
<> <>
{(editMode === 'NEW' && <div>신규:::::::::::</div>) || <div>상세:::::::::::</div>} {(editMode === 'NEW' && (
<div className="flex w-full flex-wrap md:flex-nowrap gap-4"> <form onSubmit={handleSubmit(onValid, onInvalid)}>
<div>
<div>(*필수 입력항목)</div>
<div className="form-input">
<label>담당자*</label>
<input type="text" className="input-origin" {...form.register('dispCompanyName', { required: true })} />
</div>
<div className="form-input">
<label>물건구분/물건명*</label>
<input type="radio" name="objectStatusId" value="0" id="objectStatus0" {...form.register('objectStatusId')} />
<label htmlFor="objectStatus0">신축</label>
<input type="radio" name="objectStatusId" value="1" id="objectStatus1" {...form.register('objectStatusId')} />
<label htmlFor="objectStatus0">기축</label>
<input type="text" className="input-origin" {...form.register('objectName', { required: true })} />
<div className="flex w-full max-w-xs flex-col gap-2">
<select name="objectNameOmit" {...register('objectNameOmit', { required: true })}>
<option value="">경칭선택</option>
<option value="11">111</option>
<option value="22">222</option>
<option value="33">333</option>
</select>
</div>
</div>
<div className="form-input">
<label>물건명 후리가나</label>
<input type="text" className="input-origin" {...form.register('objectNameKana', { required: true })} />
</div>
<div className="form-input">
<label>1 판매점명 / ID</label>
</div>
<div className="form-input">
<label>2 판매점명 / ID</label>
</div>
<div className="form-input">
<label>우편번호</label>
<input
type="text"
className="input-origin"
maxLength={7}
{...form.register('zipNo', {
required: true,
minLength: { value: 7, message: '7자리만가능' },
pattern: { value: /^[0-9]*$/g, message: '숫자만 입력' },
})}
/>
<Button isDisabled={!buttonValid} onClick={onSearchPostNumber}>
주소검색
</Button>
*우편번호 7자리를 입력한 , 주소검색 버튼을 클릭해 주십시오
</div>
<div className="form-input">
<label>도도부현 / 주소</label>
<div className="flex w-full flex-wrap items-end md:flex-nowrap mb-6 md:mb-0 gap-4">
{prefCodeList?.length > 0 && (
<Select className="max-w-xs" selectedKeys={prefValue} isDisabled {...form.register('prefId', { required: true })}>
{prefCodeList.map((row) => {
return <SelectItem key={row.prefId}>{row.prefName}</SelectItem>
})}
</Select>
)}
</div>
<input type="text" className="input-origin" value={form.watch('address')} {...form.register('address')} />
</div>
<div className="form-input">
<label>발전량시뮬레이션지역</label>
</div>
<div className="form-input">
<label>기준풍속</label>
</div>
<div className="form-input">
<label>수직적설량</label>
<input
type="text"
className="input-origin"
maxLength={3}
{...form.register('snowCover', {
required: true,
pattern: { value: /^[0-9]*$/g, message: '정수만 입력' },
})}
/>{' '}
cm
<Checkbox
onValueChange={(e) => {
form.setValue('coldAreaChk', e)
}}
{...form.register('coldAreaChk')}
>
한랭지대책시행
</Checkbox>
</div>
<div className="form-input">
<label>면조도구분</label>
<input type="radio" name="surfaceType" value="Ⅲ・Ⅳ" id="surfaceType0" {...form.register('surfaceType')} />
<label htmlFor="surfaceType0"></label>
<input type="radio" name="surfaceType" value="Ⅱ" id="surfaceType1" {...form.register('surfaceType')} />
<label htmlFor="surfaceType1"></label>
<Checkbox
{...form.register('saltAreaChk')}
onValueChange={(e) => {
form.setValue('saltAreaChk', e)
}}
>
염해지역용아이템사용
</Checkbox>
</div>
<div className="form-input">
<label>설치높이 installHeight</label>
</div>
<div className="form-input">
<label>계약조건</label>
<input type="radio" name="powerConTerms" value="0" id="powerConTerms0" {...form.register('powerConTerms')} />
<label htmlFor="powerConTerms0">잉여</label>
<input type="radio" name="powerConTerms" value="1" id="powerConTerms1" {...form.register('powerConTerms')} />
<label htmlFor="powerConTerms1">전량</label>
</div>
<div className="form-input">
<label>메모</label>
<Textarea
disableAutosize
classNames={{
base: 'max-w-xs',
input: 'resize-y min-h-[40px]',
}}
{...form.register('remark')}
onValueChange={(e) => {
// console.log('e::::', e)
form.setValue('remark', e)
}}
/>
</div>
</div>
<button type="submit">신규화면임시저장!!!!!!!!!</button>
</form>
)) || <div>상세:::::::::::</div>}
{/* <div className="flex w-full flex-wrap md:flex-nowrap gap-4">
<div> <div>
<span>물건번호</span> <span>물건번호</span>
<span>{objectNo}</span> <span>{objectNo}</span>
@ -200,36 +406,9 @@ export default function StuffDetail() {
<span>등록일</span> <span>등록일</span>
<span></span> <span></span>
</div> </div>
</div> </div> */}
<div>(*필수 입력항목)</div> {/* <div>(* )</div>
<div>
<span>담당자*</span>
<input type="text" placeholder="Kim Ji Young" defaultValue={detailData?.receiveUser} onChange={(e) => setReceiveUser(e.target.value)} />
</div>
<div>
<span>물건구분/물건명 *</span>
<input
type="radio"
name="radio_gubun"
id="radio_new"
value={'NEW'}
checked={gubun === 'NEW' ? true : false}
onChange={(e) => {
setGubun(e.target.value)
}}
/>
<label htmlFor="radio_new">신축</label>
<input
type="radio"
name="radio_gubun"
id="radio_old"
value={'OLD'}
checked={gubun === 'OLD' ? true : false}
onChange={(e) => {
setGubun(e.target.value)
}}
/>
<label htmlFor="radio_old">기축</label>
<div> <div>
<input type="text" placeholder="물건명" value={name2} onChange={(e) => setName2(e.target.value)} /> <input type="text" placeholder="물건명" value={name2} onChange={(e) => setName2(e.target.value)} />
</div> </div>
@ -244,8 +423,8 @@ export default function StuffDetail() {
<span>물건명 후리가나</span> <span>물건명 후리가나</span>
<input type="text" placeholder="물건명 후리가나" value={name3} onChange={(e) => setName3(e.target.value)} /> <input type="text" placeholder="물건명 후리가나" value={name3} onChange={(e) => setName3(e.target.value)} />
</div> </div>
</div> </div> */}
<div> {/* <div>
<span>판매점명 /ID *</span> <span>판매점명 /ID *</span>
<div className="flex w-full max-w-xs flex-col gap-2"> <div className="flex w-full max-w-xs flex-col gap-2">
<Autocomplete <Autocomplete
@ -258,21 +437,20 @@ export default function StuffDetail() {
{(option) => <AutocompleteItem key={option.id}>{option.name}</AutocompleteItem>} {(option) => <AutocompleteItem key={option.id}>{option.name}</AutocompleteItem>}
</Autocomplete> </Autocomplete>
</div> </div>
</div> </div> */}
<div> {/* <div>
<span>우편번호*</span> <span>우편번호*</span>
<input type="text" placeholder="숫자7자리 입력여부 체크" value={zipCode} maxLength="7" onChange={textTypeHandler} /> <input type="text" placeholder="숫자7자리 입력여부 체크" value={zipCode} maxLength="7" onChange={textTypeHandler} />
<Button onClick={onSearchPostNumber} isDisabled={buttonValid}> <Button onClick={onSearchPostNumber} isDisabled={buttonValid}>
주소검색 주소검색
</Button> </Button>
*우편번호 7자리를 입력한 , 주소검색 버튼을 클릭해 주십시오 *우편번호 7자리를 입력한 , 주소검색 버튼을 클릭해 주십시오
</div> </div> */}
<div> {/* <div>
<span>도도부현 / 주소*</span> <span>도도부현 / 주소*</span>
{/* <input type="text" placeholder="주소검색 결과 select로 변경하기 공통코드 api리턴값 selected" disabled /> */}
<input type="text" placeholder="주소검색 결과 주소 셋팅 칸" value={address2 + address3} onChange={changeAddress2} /> <input type="text" placeholder="주소검색 결과 주소 셋팅 칸" value={address2 + address3} onChange={changeAddress2} />
</div> </div> */}
<div> {/* <div>
<span>발전량시뮬레이션지역*</span> <span>발전량시뮬레이션지역*</span>
<Select label="발전량시뮬레이션지역" className="max-w-xs" onChange={(e) => setSel2(e.target.value)}> <Select label="발전량시뮬레이션지역" className="max-w-xs" onChange={(e) => setSel2(e.target.value)}>
<SelectItem key="1">111</SelectItem> <SelectItem key="1">111</SelectItem>
@ -295,8 +473,8 @@ export default function StuffDetail() {
<Checkbox isSelected={isSelected} onValueChange={setIsSelected}> <Checkbox isSelected={isSelected} onValueChange={setIsSelected}>
한랭지대책시행 한랭지대책시행
</Checkbox> </Checkbox>
</div> </div> */}
<div> {/* <div>
<span>면조도구분*</span> <span>면조도구분*</span>
<input <input
type="radio" type="radio"
@ -381,8 +559,7 @@ export default function StuffDetail() {
)} )}
<Link href="/management/stuff"> <Link href="/management/stuff">
<button type="button">물건목록</button> <button type="button">물건목록</button>
</Link> </Link> */}
{/* <Button onPress={moveList}>물건목록2</Button> */}
</> </>
) )
} }

View File

@ -82,6 +82,7 @@ export default function StuffQGrid(props) {
}, []) }, [])
const onGridReady = useCallback((event) => { const onGridReady = useCallback((event) => {
// console.log('event:::', event)
// width // width
event.api.sizeColumnsToFit() event.api.sizeColumnsToFit()
}, []) }, [])

85
src/hooks/useEvent.js Normal file
View File

@ -0,0 +1,85 @@
import { useEffect, useRef } from 'react'
import { useRecoilValue } from 'recoil'
import { canvasState, stepState } from '@/store/canvasAtom'
export function useEvent() {
const canvas = useRecoilValue(canvasState)
const step = useRecoilValue(stepState)
const keyboardEventListeners = useRef([])
useEffect(() => {
if (!canvas) {
return
}
Object.keys(canvas.__eventListeners).forEach((key) => {
if (key.indexOf('mouse') > -1) {
canvas.off(key)
}
})
removeAllKeyboardEventListeners()
addEvent(step)
}, [step])
const addEvent = (step) => {
//default Event 추가
canvas?.on('mouse:move', defaultMouseMoveEvent)
addKeyboardEventListener('keydown', document, defaultKeyboardEvent)
if (step === 1) {
canvas?.on('mouse:down', (e) => {
canvas?.add(new fabric.Rect({ width: 100, height: 100, fill: 'red', left: e.pointer.x, top: e.pointer.y }))
})
addKeyboardEventListener('keydown', document, (e) => {
if (e.key === 'Escape') {
console.log(1111)
}
})
} else if (step === 2) {
canvas?.on('mouse:down', (e) => {
canvas?.add(new fabric.Circle({ radius: 50, fill: 'blue', left: e.pointer.x, top: e.pointer.y }))
})
addKeyboardEventListener('keydown', document, (e) => {
if (e.key === 'Escape') {
console.log(2222)
}
})
} else {
canvas?.on('mouse:down', (e) => {
canvas?.add(new fabric.Triangle({ width: 100, height: 100, fill: 'green', left: e.pointer.x, top: e.pointer.y }))
})
addKeyboardEventListener('keydown', document, (e) => {
if (e.key === 'Escape') {
console.log(333)
}
})
}
}
const defaultMouseMoveEvent = (e) => {
console.log('defaultMouseMoveEvent')
}
const defaultKeyboardEvent = (e) => {
if (e.key === 'Escape') {
console.log('defaultKeyboardEvent')
}
}
/**
* document 키보드 이벤트 임의로 직접 등록한 이벤트의 경우 remove가 안되기 때문에 함수를 통해서만 등록해야 .
* @param eventType
* @param element
* @param handler
*/
function addKeyboardEventListener(eventType, element, handler) {
element.addEventListener(eventType, handler)
keyboardEventListeners.current.push({ eventType, element, handler })
}
function removeAllKeyboardEventListeners() {
keyboardEventListeners.current.forEach(({ eventType, element, handler }) => {
element.removeEventListener(eventType, handler)
})
keyboardEventListeners.current.length = 0 // 배열 초기화
}
}

View File

@ -1,25 +1,27 @@
import { useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
import { globalLocaleState } from '@/store/localeAtom' import { appMessageStore } from '@/store/localeAtom'
import KO from '@/locales/ko.json' // import KO from '@/locales/ko.json'
import JA from '@/locales/ja.json' // import JA from '@/locales/ja.json'
const SESSION_STORAGE_MESSAGE_KEY = 'QCAST_MESSAGE_STORAGE' const SESSION_STORAGE_MESSAGE_KEY = 'QCAST_MESSAGE_STORAGE'
export const useMessage = () => { export const useMessage = () => {
const globalLocale = useRecoilValue(globalLocaleState) // const globalLocale = useRecoilValue(globalLocaleState)
const appMessageState = useRecoilValue(appMessageStore)
const getMessage = (key, args = []) => { const getMessage = (key, args = []) => {
if (sessionStorage.getItem(SESSION_STORAGE_MESSAGE_KEY) === null) { // if (sessionStorage.getItem(SESSION_STORAGE_MESSAGE_KEY) === null) {
if (globalLocale === 'ko') { // if (globalLocale === 'ko') {
setSessionMessage(JSON.stringify(KO)) // setSessionMessage(JSON.stringify(KO))
} else { // } else {
setSessionMessage(JSON.stringify(JA)) // setSessionMessage(JSON.stringify(JA))
} // }
} // }
const sessionMessage = getSessionMessage() // const sessionMessage = getSessionMessage()
const message = sessionMessage[key] || key // const message = sessionMessage[key] || key
const message = appMessageState[key] || key
return args.reduce((acc, arg, i) => { return args.reduce((acc, arg, i) => {
return acc.replaceAll(`{${i}}`, arg) return acc.replaceAll(`{${i}}`, arg)

View File

@ -1,5 +1,6 @@
{ {
"hi": "こんにちは", "hi": "こんにちは",
"welcome": "환영합니다. {0}님",
"common.message.no.data": "No data", "common.message.no.data": "No data",
"common.message.no.dataDown": "ダウンロードするデータがありません", "common.message.no.dataDown": "ダウンロードするデータがありません",
"common.message.noData": "表示するデータがありません", "common.message.noData": "表示するデータがありません",
@ -86,5 +87,70 @@
"common.message.backToG3": "Back to G3処理実行しますか", "common.message.backToG3": "Back to G3処理実行しますか",
"common.message.writeToConfirm": "作成解除を実行しますか?", "common.message.writeToConfirm": "作成解除を実行しますか?",
"common.message.password.init.success": "パスワード [{0}] に初期化されました。", "common.message.password.init.success": "パスワード [{0}] に初期化されました。",
"common.message.no.edit.save": "この文書は変更できません。" "common.message.no.edit.save": "この文書は変更できません。",
"common.require": "필수",
"site.name": "Q.CAST III",
"site.sub_name": "태양광 발전 시스템 도면관리 사이트",
"login": "로그인",
"login.init_password.btn": "비밀번호 초기화",
"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": "전화번호",
"join.sub1.telNo_placeholder": "00-0000-0000",
"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.sub2.telNo_placeholder": "00-0000-0000",
"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",
"stuff.gridHeader.lastEditDatetime": "갱신일시",
"stuff.gridHeader.objectNo": "물건번호",
"stuff.gridHeader.planTotCnt": "플랜 수",
"stuff.gridHeader.objectName": "물건명",
"stuff.gridHeader.saleStoreId": "대리점ID",
"stuff.gridHeader.saleStoreName": "대리점명",
"stuff.gridHeader.address": "물건주소",
"stuff.gridHeader.dispCompanyName": "견적처",
"stuff.gridHeader.receiveUser": "담당자",
"stuff.gridHeader.specDate": "사양확인",
"stuff.gridHeader.createDatetime": "등록일"
} }

View File

@ -1,5 +1,6 @@
{ {
"hi": "안녕하세요", "hi": "안녕하세요",
"welcome": "환영합니다. {0}님",
"common.message.no.data": "No data", "common.message.no.data": "No data",
"common.message.no.dataDown": "No data to download", "common.message.no.dataDown": "No data to download",
"common.message.noData": "No data to display", "common.message.noData": "No data to display",
@ -86,5 +87,70 @@
"common.message.backToG3": "Back to G3 처리를 실행하시겠습니까?", "common.message.backToG3": "Back to G3 처리를 실행하시겠습니까?",
"common.message.writeToConfirm": "작성 해제를 실행하시겠습니까?", "common.message.writeToConfirm": "작성 해제를 실행하시겠습니까?",
"common.message.password.init.success": "비밀번호 [{0}]로 초기화 되었습니다.", "common.message.password.init.success": "비밀번호 [{0}]로 초기화 되었습니다.",
"common.message.no.edit.save": "This document cannot be changed." "common.message.no.edit.save": "This document cannot be changed.",
"common.require": "필수",
"site.name": "Q.CAST III",
"site.sub_name": "태양광 발전 시스템 도면관리 사이트",
"login": "로그인",
"login.init_password.btn": "비밀번호 초기화",
"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": "전화번호",
"join.sub1.telNo_placeholder": "00-0000-0000",
"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.sub2.telNo_placeholder": "00-0000-0000",
"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",
"stuff.gridHeader.lastEditDatetime": "갱신일시",
"stuff.gridHeader.objectNo": "물건번호",
"stuff.gridHeader.planTotCnt": "플랜 수",
"stuff.gridHeader.objectName": "물건명",
"stuff.gridHeader.saleStoreId": "대리점ID",
"stuff.gridHeader.saleStoreName": "대리점명",
"stuff.gridHeader.address": "물건주소",
"stuff.gridHeader.dispCompanyName": "견적처",
"stuff.gridHeader.receiveUser": "담당자",
"stuff.gridHeader.specDate": "사양확인",
"stuff.gridHeader.createDatetime": "등록일"
} }

View File

@ -179,3 +179,8 @@ export const objectPlacementModeState = atom({
key: 'objectPlacementMode', key: 'objectPlacementMode',
default: { width: 0, height: 0, areaBoundary: false, inputType: 'free', batchType: 'opening' }, default: { width: 0, height: 0, areaBoundary: false, inputType: 'free', batchType: 'opening' },
}) })
export const stepState = atom({
key: 'step',
default: 0,
})

View File

@ -4,3 +4,8 @@ export const globalLocaleState = atom({
key: 'globalLocaleState', key: 'globalLocaleState',
default: 'ko', default: 'ko',
}) })
export const appMessageStore = atom({
key: 'appMessageState',
default: {},
})

View File

@ -10,6 +10,14 @@ export const isObjectNotEmpty = (obj) => {
return Object.keys(obj).length > 0 return Object.keys(obj).length > 0
} }
export const isNotEmptyArray = (array) => {
return Array.isArray(array) && array.length
}
export const isEmptyArray = (array) => {
return !isNotEmptyArray(array)
}
/** /**
* ex) const params = {page:10, searchDvsnCd: 20} * ex) const params = {page:10, searchDvsnCd: 20}
* @param {*} params * @param {*} params

View File

@ -5515,11 +5515,6 @@ react-dom@^18:
loose-envify "^1.1.0" loose-envify "^1.1.0"
scheduler "^0.23.2" scheduler "^0.23.2"
react-hook-form@^7.53.0:
version "7.53.0"
resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.53.0.tgz#3cf70951bf41fa95207b34486203ebefbd3a05ab"
integrity sha512-M1n3HhqCww6S2hxLxciEXy2oISPnAzxY7gvwVPrtlczTM/1dDadXgUxDpHMrMTblDOcm/AXtXxHwZ3jpg1mqKQ==
react-draggable@^4.4.6: react-draggable@^4.4.6:
version "4.4.6" version "4.4.6"
resolved "https://registry.yarnpkg.com/react-draggable/-/react-draggable-4.4.6.tgz#63343ee945770881ca1256a5b6fa5c9f5983fe1e" resolved "https://registry.yarnpkg.com/react-draggable/-/react-draggable-4.4.6.tgz#63343ee945770881ca1256a5b6fa5c9f5983fe1e"
@ -5528,6 +5523,11 @@ react-draggable@^4.4.6:
clsx "^1.1.1" clsx "^1.1.1"
prop-types "^15.8.1" prop-types "^15.8.1"
react-hook-form@^7.53.0:
version "7.53.0"
resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.53.0.tgz#3cf70951bf41fa95207b34486203ebefbd3a05ab"
integrity sha512-M1n3HhqCww6S2hxLxciEXy2oISPnAzxY7gvwVPrtlczTM/1dDadXgUxDpHMrMTblDOcm/AXtXxHwZ3jpg1mqKQ==
react-icons@^5.3.0: react-icons@^5.3.0:
version "5.3.0" version "5.3.0"
resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-5.3.0.tgz#ccad07a30aebd40a89f8cfa7d82e466019203f1c" resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-5.3.0.tgz#ccad07a30aebd40a89f8cfa7d82e466019203f1c"