diff --git a/Makefile b/Makefile index 579954f39..56105ca22 100644 --- a/Makefile +++ b/Makefile @@ -52,8 +52,14 @@ setup: ## 開発環境をセットアップ (build > run-db > run) @echo "$(GREEN)--- Setup completed! ---$(RESET)" ##@ 基本操作 +fix-perms: ## ボリュームの所有権を一般ユーザー(1000)に変更 + @echo "$(GREEN)--- Fixing volume permissions... ---$(RESET)" + docker compose run --rm --user root view chown -R 1000:1000 /app/.pnpm-store + docker compose run --rm --user root api chown -R 1000:1000 /go/cache /go/pkg/mod + build: ## アプリコンテナのイメージをビルド docker compose build + make fix-perms docker compose run --rm view pnpm install build-stg: ## ステージング環境ビルド @@ -71,7 +77,9 @@ build-run: ## ビルドと起動を同時実行 run-rebuild: ## ボリューム削除→ビルド→起動 docker compose down -v - docker compose up --build + docker compose build + make fix-perms + docker compose up -d restart: ## アプリコンテナの再起動 (DBは維持) @echo "$(GREEN)--- Restarting App Containers ---$(RESET)" diff --git a/view/next-project/README.md b/view/next-project/README.md index c87e0421d..d263d759f 100644 --- a/view/next-project/README.md +++ b/view/next-project/README.md @@ -5,9 +5,7 @@ This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next First, run the development server: ```bash -npm run dev -# or -yarn dev +pnpm dev ``` Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. diff --git a/view/next-project/package.json b/view/next-project/package.json index 788cb0687..df408a99b 100644 --- a/view/next-project/package.json +++ b/view/next-project/package.json @@ -44,10 +44,9 @@ "react-pdf": "^9.2.1", "react-router-dom": "^6.0.2", "react-select": "^5.7.3", - "recoil": "^0.7.6", - "recoil-persist": "^4.2.0", "swr": "^2.3.0", - "tailwindcss": "^3.1.6" + "tailwindcss": "^3.1.6", + "zustand": "^5.0.10" }, "devDependencies": { "@chromatic-com/storybook": "^1.4.0", diff --git a/view/next-project/pnpm-lock.yaml b/view/next-project/pnpm-lock.yaml index 24f907ea1..7638a0858 100644 --- a/view/next-project/pnpm-lock.yaml +++ b/view/next-project/pnpm-lock.yaml @@ -95,18 +95,15 @@ importers: react-select: specifier: ^5.7.3 version: 5.10.2(@types/react@18.3.27)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - recoil: - specifier: ^0.7.6 - version: 0.7.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - recoil-persist: - specifier: ^4.2.0 - version: 4.2.0(recoil@0.7.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) swr: specifier: ^2.3.0 version: 2.3.8(react@18.3.1) tailwindcss: specifier: ^3.1.6 version: 3.4.19(yaml@2.8.2) + zustand: + specifier: ^5.0.10 + version: 5.0.10(@types/react@18.3.27)(immer@9.0.21)(react@18.3.1)(use-sync-external-store@1.6.0(react@18.3.1)) devDependencies: '@chromatic-com/storybook': specifier: ^1.4.0 @@ -3520,9 +3517,6 @@ packages: graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - hamt_plus@1.0.2: - resolution: {integrity: sha512-t2JXKaehnMb9paaYA7J0BX8QQAY8lwfQ9Gjf4pg/mk4krt+cmwmU652HOoWonf+7+EQV97ARPMhhVgU1ra2GhA==} - has-bigints@1.1.0: resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} engines: {node: '>= 0.4'} @@ -4874,23 +4868,6 @@ packages: resolution: {integrity: sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==} engines: {node: '>= 4'} - recoil-persist@4.2.0: - resolution: {integrity: sha512-MHVfML9GxJP3RpkKR4F5rp7DtvzIvjWhowtMao/b7h2k4afMio/4sMAdUtltIrDaeVegH0Iga8Sx5XQ3oD7CzA==} - peerDependencies: - recoil: ^0.7.2 - - recoil@0.7.7: - resolution: {integrity: sha512-8Og5KPQW9LwC577Vc7Ug2P0vQshkv1y3zG3tSSkWMqkWSwHmE+by06L8JtnGocjW6gcCvfwB3YtrJG6/tWivNQ==} - peerDependencies: - react: '>=16.13.1' - react-dom: '*' - react-native: '*' - peerDependenciesMeta: - react-dom: - optional: true - react-native: - optional: true - redent@3.0.0: resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} engines: {node: '>=8'} @@ -5822,6 +5799,24 @@ packages: yoga-layout@2.0.1: resolution: {integrity: sha512-tT/oChyDXelLo2A+UVnlW9GU7CsvFMaEnd9kVFsaiCQonFAXd3xrHhkLYu+suwwosrAEQ746xBU+HvYtm1Zs2Q==} + zustand@5.0.10: + resolution: {integrity: sha512-U1AiltS1O9hSy3rul+Ub82ut2fqIAefiSuwECWt6jlMVUGejvf+5omLcRBSzqbRagSM3hQZbtzdeRc6QVScXTg==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + snapshots: '@adobe/css-tools@4.4.4': {} @@ -10023,8 +10018,6 @@ snapshots: graphemer@1.4.0: {} - hamt_plus@1.0.2: {} - has-bigints@1.1.0: {} has-flag@4.0.0: {} @@ -11446,17 +11439,6 @@ snapshots: tiny-invariant: 1.3.3 tslib: 2.8.1 - recoil-persist@4.2.0(recoil@0.7.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)): - dependencies: - recoil: 0.7.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - - recoil@0.7.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - hamt_plus: 1.0.2 - react: 18.3.1 - optionalDependencies: - react-dom: 18.3.1(react@18.3.1) - redent@3.0.0: dependencies: indent-string: 4.0.0 @@ -12520,3 +12502,10 @@ snapshots: yocto-queue@1.2.2: {} yoga-layout@2.0.1: {} + + zustand@5.0.10(@types/react@18.3.27)(immer@9.0.21)(react@18.3.1)(use-sync-external-store@1.6.0(react@18.3.1)): + optionalDependencies: + '@types/react': 18.3.27 + immer: 9.0.21 + react: 18.3.1 + use-sync-external-store: 1.6.0(react@18.3.1) diff --git a/view/next-project/src/components/auth/SignInView.tsx b/view/next-project/src/components/auth/SignInView.tsx index b6c6d249b..775a509d0 100644 --- a/view/next-project/src/components/auth/SignInView.tsx +++ b/view/next-project/src/components/auth/SignInView.tsx @@ -1,9 +1,8 @@ import Router from 'next/router'; import { useState } from 'react'; import { useForm } from 'react-hook-form'; -import { useRecoilState } from 'recoil'; -import { authAtom, userAtom } from '@/store/atoms'; +import { useAuthStore, useUserStore } from '@/store'; import { get_with_token } from '@api/api_methods'; import { signIn } from '@api/signIn'; import LoadingButton from '@components/common/LoadingButton'; @@ -14,8 +13,8 @@ import { PrimaryButton } from '../common'; export default function SignInView() { // ログイン中フラグ const [isSignInNow, setIsSignInNow] = useState(false); - const [, setAuth] = useRecoilState(authAtom); - const [, setUser] = useRecoilState(userAtom); + const setAuth = useAuthStore((state) => state.setAuth); + const setUser = useUserStore((state) => state.setUser); const { register, diff --git a/view/next-project/src/components/auth/SignUpView.tsx b/view/next-project/src/components/auth/SignUpView.tsx index 8e6858050..f599080da 100644 --- a/view/next-project/src/components/auth/SignUpView.tsx +++ b/view/next-project/src/components/auth/SignUpView.tsx @@ -1,10 +1,9 @@ import Router from 'next/router'; import React, { useState } from 'react'; import { useForm } from 'react-hook-form'; -import { useRecoilState } from 'recoil'; import { BUREAUS } from '@/constants/bureaus'; -import { authAtom, userAtom } from '@/store/atoms'; +import { useAuthStore, useUserStore } from '@/store'; import { get } from '@api/api_methods'; import { signUp } from '@api/signUp'; import { post } from '@api/user'; @@ -14,8 +13,8 @@ import { SignUp, User } from '@type/common'; import { PrimaryButton } from '../common'; export default function SignUpView() { - const [, setAuth] = useRecoilState(authAtom); - const [, setUser] = useRecoilState(userAtom); + const setAuth = useAuthStore((state) => state.setAuth); + const setUser = useUserStore((state) => state.setUser); // 新規登録中フラグ const [isSignUpNow, setIsSignUpNow] = useState(false); diff --git a/view/next-project/src/components/common/ChakraUIDropdown.tsx b/view/next-project/src/components/common/ChakraUIDropdown.tsx index 089ca9f22..4357225f0 100644 --- a/view/next-project/src/components/common/ChakraUIDropdown.tsx +++ b/view/next-project/src/components/common/ChakraUIDropdown.tsx @@ -1,9 +1,8 @@ import { Button, Menu, MenuButton, MenuItem, MenuList } from '@chakra-ui/react'; import React from 'react'; import { RiArrowDropDownLine } from 'react-icons/ri'; -import { useRecoilValue } from 'recoil'; -import { userAtom } from '@/store/atoms'; +import { useUserStore } from '@/store'; interface Props { title: string; @@ -12,7 +11,7 @@ interface Props { } const Dropdown = (props: Props) => { - const user = useRecoilValue(userAtom); + const user = useUserStore((state) => state.user); return ( diff --git a/view/next-project/src/components/common/Header/Header.tsx b/view/next-project/src/components/common/Header/Header.tsx index 0d0f9f31a..45fe66975 100644 --- a/view/next-project/src/components/common/Header/Header.tsx +++ b/view/next-project/src/components/common/Header/Header.tsx @@ -2,30 +2,28 @@ import Image from 'next/image'; import Router from 'next/router'; import { AiOutlineMenu } from 'react-icons/ai'; import { RiAccountCircleFill } from 'react-icons/ri'; -import { useRecoilState } from 'recoil'; -import { authAtom, userAtom } from '@/store/atoms'; +import { useAuthStore, useUserStore } from '@/store'; import { del } from '@api/signOut'; import { ChakraUIDropdown } from '@components/common'; -import { User } from '@type/common'; import { HeaderProps } from './Header.type'; const Header = (props: HeaderProps) => { const { onSideNavOpen } = props; - const [auth, setAuth] = useRecoilState(authAtom); - const [user, setUser] = useRecoilState(userAtom); + const { accessToken, setAuth } = useAuthStore(); + const { user, resetUser } = useUserStore(); const signOut = async () => { const signOutUrl: string = process.env.CSR_API_URI + '/mail_auth/signout'; - const req = await del(signOutUrl, auth.accessToken); + const req = await del(signOutUrl, accessToken); const authData = { isSignIn: false, accessToken: '', }; if (req.status === 200) { setAuth(authData); - setUser({} as User); + resetUser(); Router.push('/'); } }; diff --git a/view/next-project/src/components/create_purchase_report/usePurchaseReportForm.ts b/view/next-project/src/components/create_purchase_report/usePurchaseReportForm.ts index 06c71aff7..8c7994295 100644 --- a/view/next-project/src/components/create_purchase_report/usePurchaseReportForm.ts +++ b/view/next-project/src/components/create_purchase_report/usePurchaseReportForm.ts @@ -1,16 +1,15 @@ import { NextRouter } from 'next/router'; -import { useState, useEffect } from 'react'; -import { useRecoilValue } from 'recoil'; +import { useEffect, useState } from 'react'; import { + useGetBuyReportsId, useGetDivisionsUsers, useGetFestivalItemsUsers, - useGetBuyReportsId, usePostBuyReports, usePutBuyReportsId, } from '@/generated/hooks'; import { DivisionOption, FestivalItemOption } from '@/generated/model'; -import { userAtom } from '@/store/atoms'; +import { useCurrentUser } from '@/store'; import type { BuyReport, PostBuyReportsBody, PutBuyReportsIdBody } from '@/generated/model'; @@ -49,7 +48,7 @@ export const usePurchaseReportForm = (router: NextRouter) => { const reportId = reportIdParam ? Number(reportIdParam) : undefined; const isEditMode = from === 'purchase_report_list'; - const user = useRecoilValue(userAtom); + const user = useCurrentUser(); const [departments, setDepartments] = useState([]); const [uploadedFile, setUploadedFile] = useState(null); const [festivalItems, setFestivalItems] = useState([]); diff --git a/view/next-project/src/components/layout/MainLayout/MainLayout.tsx b/view/next-project/src/components/layout/MainLayout/MainLayout.tsx index e74ebddd8..ee64a8b63 100644 --- a/view/next-project/src/components/layout/MainLayout/MainLayout.tsx +++ b/view/next-project/src/components/layout/MainLayout/MainLayout.tsx @@ -2,50 +2,74 @@ import clsx from 'clsx'; import Head from 'next/head'; import { useRouter } from 'next/router'; import React, { useEffect, useState } from 'react'; -import { useRecoilState } from 'recoil'; -import { authAtom, userAtom } from '@/store/atoms'; -import 'tailwindcss/tailwind.css'; -import { Header, SideNav } from '@components/common'; -import { User } from '@type/common'; +import { useAuthStore, useUserStore } from '@/store'; +import { Header, Loading, SideNav } from '@components/common'; import { get_with_token_valid } from '@utils/api/api_methods'; +import 'tailwindcss/tailwind.css'; import s from './MainLayout.module.css'; +const CURRENT_USER_URL = process.env.CSR_API_URI + '/current_user'; + interface LayoutProps { children?: React.ReactNode; } export default function MainLayout(props: LayoutProps) { const router = useRouter(); - const [auth, setAuth] = useRecoilState(authAtom); - const [_, setUser] = useRecoilState(userAtom); + const { isSignIn, accessToken, resetAuth, _hasHydrated: authHasHydrated } = useAuthStore(); + const { resetUser, _hasHydrated: userHasHydrated } = useUserStore(); const [isSideNavOpen, setIsSideNavOpen] = useState(true); + const [isChecking, setIsChecking] = useState(true); + + const isLoginPage = router.pathname === '/'; + + const hasHydrated = authHasHydrated && userHasHydrated; + useEffect(() => { - const getCurrentUserUrl = process.env.CSR_API_URI + '/current_user'; - get_with_token_valid(getCurrentUserUrl, auth.accessToken).then((result) => { - if (!result) { - localStorage.clear(); - const authData = { - isSignIn: false, - accessToken: '', - }; - setAuth(authData); - setUser({} as User); - router.push('/'); + if (!hasHydrated) return; + + const validateSession = async () => { + if (!isSignIn || !accessToken) { + await handleLogout(); + return; + } + + const isValid = await get_with_token_valid(CURRENT_USER_URL, accessToken); + + if (!isValid) { + await handleLogout(); + } else if (router.pathname === '/') { + await router.push('/my_page'); + setIsChecking(false); } else { - if (router.isReady) { - if (!auth.isSignIn) { - router.push('/'); - localStorage.clear(); - } else if (auth.isSignIn === true && router.pathname == '/') { - router.push('/purchaseorders'); - } - } + setIsChecking(false); + } + }; + + const handleLogout = async () => { + resetAuth(); + resetUser(); + + if (!isLoginPage) { + await router.push('/'); } - }); - }, [router]); + setIsChecking(false); + }; + + validateSession(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [hasHydrated, router.pathname, isSignIn, accessToken, resetAuth, resetUser]); + + if (!hasHydrated || isChecking) { + return ( +
+ +
+ ); + } return ( <> @@ -56,19 +80,23 @@ export default function MainLayout(props: LayoutProps) {
-
setIsSideNavOpen(!isSideNavOpen)} /> + {!isLoginPage &&
setIsSideNavOpen(!isSideNavOpen)} />}
+ {!isLoginPage && ( +
+ +
+ )}
- -
-
{props.children}
diff --git a/view/next-project/src/components/purchasereports/DetailPage1.tsx b/view/next-project/src/components/purchasereports/DetailPage1.tsx index 1165ce133..e3afff9f7 100644 --- a/view/next-project/src/components/purchasereports/DetailPage1.tsx +++ b/view/next-project/src/components/purchasereports/DetailPage1.tsx @@ -2,14 +2,13 @@ import { useRouter } from 'next/router'; import React, { FC, useEffect, useMemo, useState } from 'react'; import { FaChevronCircleRight } from 'react-icons/fa'; import { RiExternalLinkLine, RiFileCopyLine } from 'react-icons/ri'; -import { useRecoilState } from 'recoil'; import PrimaryButton from '@/components/common/OutlinePrimaryButton/OutlinePrimaryButton'; -import { userAtom } from '@/store/atoms'; +import { useCurrentUser } from '@/store'; import { downloadFile } from '@/utils/downloadFile'; import { del } from '@api/api_methods'; import { Checkbox, RedButton, Tooltip } from '@components/common'; -import { PurchaseItem, PurchaseReport, PurchaseReportView, Expense } from '@type/common'; +import { Expense, PurchaseItem, PurchaseReport, PurchaseReportView } from '@type/common'; import { createPurchaseReportFormPdf } from '@utils/createPurchaseReportPdf'; interface DetailModalProps { @@ -22,7 +21,7 @@ interface DetailModalProps { } const DetailPage1: FC = (props) => { - const [user] = useRecoilState(userAtom); + const user = useCurrentUser(); const [date, setDate] = useState(String); const [japaneseDate, setJapaneseDate] = useState(String); diff --git a/view/next-project/src/components/purchasereports/EditModal.tsx b/view/next-project/src/components/purchasereports/EditModal.tsx index 7ba9f9016..c53b5faf9 100644 --- a/view/next-project/src/components/purchasereports/EditModal.tsx +++ b/view/next-project/src/components/purchasereports/EditModal.tsx @@ -1,9 +1,8 @@ import { useRouter } from 'next/router'; import React, { useCallback, useEffect, useState } from 'react'; import { RiArrowDropRightLine } from 'react-icons/ri'; -import { useRecoilState } from 'recoil'; -import { userAtom } from '@/store/atoms'; +import { useCurrentUser } from '@/store'; import { get, put as putPurchaseReport } from '@api/api_methods'; import { put as putPurchaseItem } from '@api/purchaseItem'; import { @@ -26,7 +25,7 @@ interface ModalProps { } export default function EditModal(props: ModalProps) { - const [user] = useRecoilState(userAtom); + const user = useCurrentUser(); const router = useRouter(); diff --git a/view/next-project/src/components/purchasereports/PurchaseReportAddModal.tsx b/view/next-project/src/components/purchasereports/PurchaseReportAddModal.tsx index 44368b657..eab81c5e1 100644 --- a/view/next-project/src/components/purchasereports/PurchaseReportAddModal.tsx +++ b/view/next-project/src/components/purchasereports/PurchaseReportAddModal.tsx @@ -1,9 +1,8 @@ import { useRouter } from 'next/router'; import React, { useCallback, useEffect, useState } from 'react'; import { RiArrowDropRightLine } from 'react-icons/ri'; -import { useRecoilState } from 'recoil'; -import { userAtom } from '@/store/atoms'; +import { useCurrentUser } from '@/store'; import { get, post } from '@api/api_methods'; import { post as postItem, put } from '@api/purchaseItem'; import { post as postOrder } from '@api/purchaseOrder'; @@ -36,7 +35,7 @@ interface ModalProps { } export default function PurchaseReportAddModal(props: ModalProps) { - const [user] = useRecoilState(userAtom); + const user = useCurrentUser(); const router = useRouter(); diff --git a/view/next-project/src/components/purchasereports/PurchaseReportItemNumModal.tsx b/view/next-project/src/components/purchasereports/PurchaseReportItemNumModal.tsx index f06743da4..e843f2fb6 100644 --- a/view/next-project/src/components/purchasereports/PurchaseReportItemNumModal.tsx +++ b/view/next-project/src/components/purchasereports/PurchaseReportItemNumModal.tsx @@ -1,7 +1,6 @@ -import React, { useState, useEffect } from 'react'; -import { useRecoilState } from 'recoil'; +import React, { useEffect, useState } from 'react'; -import { userAtom } from '@/store/atoms'; +import { useCurrentUser } from '@/store'; import { CloseButton, Modal, @@ -12,7 +11,7 @@ import { } from '@components/common'; import PurchaseReportAddModal from '@components/purchasereports/PurchaseReportAddModal'; import { useUI } from '@components/ui/context'; -import { PurchaseOrder, Expense, YearPeriod } from '@type/common'; +import { Expense, PurchaseOrder, YearPeriod } from '@type/common'; import { get } from '@utils/api/api_methods'; export default function PurchaseReportItemNumModal() { @@ -30,7 +29,7 @@ export default function PurchaseReportItemNumModal() { getPeriods(); }, []); - const [user] = useRecoilState(userAtom); + const user = useCurrentUser(); const [expenses, setExpenses] = useState([]); const [expenseID, setExpenseID] = useState(0); diff --git a/view/next-project/src/components/yearperiods/AddModal.tsx b/view/next-project/src/components/yearperiods/AddModal.tsx index d5c7cb58a..c6dae373a 100644 --- a/view/next-project/src/components/yearperiods/AddModal.tsx +++ b/view/next-project/src/components/yearperiods/AddModal.tsx @@ -1,13 +1,12 @@ import { format } from 'date-fns'; import { useRouter } from 'next/router'; import React, { Dispatch, FC, SetStateAction, useState } from 'react'; -import { useRecoilState } from 'recoil'; -import { userAtom } from '@/store/atoms'; +import { useUserStore } from '@/store'; import { YearPeriod } from '@/type/common'; import { post } from '@/utils/api/api_methods'; -import { Modal, CloseButton, Input, PrimaryButton } from '../common'; +import { CloseButton, Input, Modal, PrimaryButton } from '../common'; interface ModalProps { setShowModal: Dispatch>; @@ -15,7 +14,7 @@ interface ModalProps { } const OpenAddModal: FC = (props) => { - const [user] = useRecoilState(userAtom); + const user = useUserStore((state) => state.user); const [flashMessage, setFlashMessage] = useState(''); const router = useRouter(); diff --git a/view/next-project/src/pages/_app.tsx b/view/next-project/src/pages/_app.tsx index e66754adc..d74c92c55 100644 --- a/view/next-project/src/pages/_app.tsx +++ b/view/next-project/src/pages/_app.tsx @@ -2,7 +2,6 @@ import { ChakraProvider } from '@chakra-ui/react'; import localFont from 'next/font/local'; import Head from 'next/head'; import { NuqsAdapter } from 'nuqs/adapters/next/pages'; -import { RecoilRoot } from 'recoil'; import Layout from '@components/layout/Layout'; import { ManagedUIContext } from '@components/ui/context'; @@ -18,20 +17,18 @@ export const notoSansJP = localFont({ function MyApp({ Component, pageProps }: AppProps) { return ( - - - - - - - - - - - - - - + + + + + + + + + + + + ); } diff --git a/view/next-project/src/pages/budget_managements/index.tsx b/view/next-project/src/pages/budget_managements/index.tsx index bb3ce48f1..c60f4ba8b 100644 --- a/view/next-project/src/pages/budget_managements/index.tsx +++ b/view/next-project/src/pages/budget_managements/index.tsx @@ -1,9 +1,8 @@ import router from 'next/router'; -import { useRecoilValue } from 'recoil'; import BudgetManagement from '@/components/budget_managements/BudgetManagement'; import MainLayout from '@/components/layout/MainLayout'; -import { userAtom } from '@/store/atoms'; +import { useCurrentUser } from '@/store'; import { Year } from '@/type/common'; import { get } from '@/utils/api/api_methods'; @@ -22,7 +21,7 @@ export async function getServerSideProps() { } export default function Home(props: Props) { - const user = useRecoilValue(userAtom); + const user = useCurrentUser(); user?.roleID === 1 && router.push('/my_page'); return ( diff --git a/view/next-project/src/pages/fund_informations/index.tsx b/view/next-project/src/pages/fund_informations/index.tsx index cfbeb7aac..7474505c8 100644 --- a/view/next-project/src/pages/fund_informations/index.tsx +++ b/view/next-project/src/pages/fund_informations/index.tsx @@ -1,15 +1,14 @@ import Head from 'next/head'; import router from 'next/router'; -import { useRecoilValue } from 'recoil'; -import { FundInformationTable, FundInformationHeader } from '@/components/fund_information'; +import { FundInformationHeader, FundInformationTable } from '@/components/fund_information'; import { useFundInformations } from '@/components/fund_information/useFundInformations'; -import { userAtom } from '@/store/atoms'; +import { useCurrentUser } from '@/store'; import { Card } from '@components/common'; import MainLayout from '@components/layout/MainLayout'; export default function FundInformations() { - const user = useRecoilValue(userAtom); + const user = useCurrentUser(); user?.roleID === 1 && router.push('/my_page'); diff --git a/view/next-project/src/pages/my_page/index.tsx b/view/next-project/src/pages/my_page/index.tsx index 1f8f36c41..7d27a68cd 100644 --- a/view/next-project/src/pages/my_page/index.tsx +++ b/view/next-project/src/pages/my_page/index.tsx @@ -1,20 +1,19 @@ import { useRouter } from 'next/router'; import { useEffect, useState } from 'react'; import { RiAddCircleLine } from 'react-icons/ri'; -import { useRecoilValue } from 'recoil'; import { Card, Loading, PrimaryButton } from '@/components/common'; import MainLayout from '@/components/layout/MainLayout'; import TableSection from '@/components/mypage/TableSection'; import { useGetFestivalItemsDetailsUserId } from '@/generated/hooks'; -import { userAtom } from '@/store/atoms'; +import { useCurrentUser } from '@/store'; import { User } from '@/type/common'; import { notoSansJP } from '../_app'; const MyPage = () => { const router = useRouter(); - const user = useRecoilValue(userAtom); + const user = useCurrentUser(); const [currentUser, setCurrentUser] = useState(); useEffect(() => { diff --git a/view/next-project/src/pages/purchase_report_list/index.tsx b/view/next-project/src/pages/purchase_report_list/index.tsx index e583e3e1c..149eb2f81 100644 --- a/view/next-project/src/pages/purchase_report_list/index.tsx +++ b/view/next-project/src/pages/purchase_report_list/index.tsx @@ -1,8 +1,7 @@ import { saveAs } from 'file-saver'; import { useRouter } from 'next/router'; -import { useCallback, useState, useEffect, useMemo } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { TbDownload } from 'react-icons/tb'; -import { useRecoilValue } from 'recoil'; import DownloadButton from '@/components/common/DownloadButton'; import PrimaryButton from '@/components/common/OutlinePrimaryButton/OutlinePrimaryButton'; @@ -12,14 +11,14 @@ import { useGetYearsPeriods, usePutBuyReportStatusBuyReportId, } from '@/generated/hooks'; -import { userAtom } from '@/store/atoms'; +import { useCurrentUser } from '@/store'; import { Card, Checkbox, EditButton, Loading, Title } from '@components/common'; import MainLayout from '@components/layout/MainLayout'; import OpenDeleteModalButton from '@components/purchasereports/OpenDeleteModalButton'; import type { - GetBuyReportsDetailsParams, BuyReportDetail, + GetBuyReportsDetailsParams, PutBuyReportStatusBuyReportIdBody, } from '@/generated/model'; @@ -31,7 +30,7 @@ export default function PurchaseReports() { error: yearPeriodsError, } = useGetYearsPeriods(); const yearPeriods = yearPeriodsData?.data; - const user = useRecoilValue(userAtom); + const user = useCurrentUser(); user?.roleID === 1 && router.push('/my_page'); diff --git a/view/next-project/src/pages/teachers/index.tsx b/view/next-project/src/pages/teachers/index.tsx index 19e391f2d..c4f6dcd52 100644 --- a/view/next-project/src/pages/teachers/index.tsx +++ b/view/next-project/src/pages/teachers/index.tsx @@ -1,9 +1,8 @@ import clsx from 'clsx'; import Head from 'next/head'; -import { useState, useEffect } from 'react'; -import { useRecoilValue } from 'recoil'; +import { useEffect, useState } from 'react'; -import { userAtom } from '@/store/atoms'; +import { useCurrentUser } from '@/store'; import { get } from '@api/api_methods'; import { Card, Title } from '@components/common'; import MainLayout from '@components/layout/MainLayout'; @@ -42,7 +41,7 @@ export default function TeachersList(props: Props) { departments[0], ); - const user = useRecoilValue(userAtom); + const user = useCurrentUser(); const [currentUser, setCurrentUser] = useState(); const isDisabled = !( currentUser?.roleID === 2 || diff --git a/view/next-project/src/pages/users/index.tsx b/view/next-project/src/pages/users/index.tsx index 7a1fdcf85..c13e13afe 100644 --- a/view/next-project/src/pages/users/index.tsx +++ b/view/next-project/src/pages/users/index.tsx @@ -1,11 +1,10 @@ import clsx from 'clsx'; import Head from 'next/head'; import { useRouter } from 'next/router'; -import { useEffect, useState, useMemo } from 'react'; -import { useRecoilValue } from 'recoil'; +import { useEffect, useMemo, useState } from 'react'; import OpenDeleteModalButton from '@/components/users/OpenDeleteModalButton'; -import { userAtom } from '@/store/atoms'; +import { useCurrentUser } from '@/store'; import { get } from '@api/api_methods'; import { Card, Title } from '@components/common'; import MainLayout from '@components/layout/MainLayout/MainLayout'; @@ -36,7 +35,7 @@ export default function Users(props: Props) { const { users, bureaus } = props; const router = useRouter(); - const user = useRecoilValue(userAtom); + const user = useCurrentUser(); const [currentUser, setCurrentUser] = useState(); const [selectedBureau, setSelectedBureau] = useState(0); const [filterUsers, setFilterUsers] = useState(users); diff --git a/view/next-project/src/pages/yearperiods/index.tsx b/view/next-project/src/pages/yearperiods/index.tsx index f676fc47f..a0cf7e956 100644 --- a/view/next-project/src/pages/yearperiods/index.tsx +++ b/view/next-project/src/pages/yearperiods/index.tsx @@ -1,17 +1,16 @@ import clsx from 'clsx'; import Head from 'next/head'; import { useRouter } from 'next/router'; -import { useEffect, useState, useMemo } from 'react'; -import { useRecoilValue } from 'recoil'; +import { useEffect, useMemo, useState } from 'react'; import OpenAddModalButton from '@/components/yearperiods/OpenAddModalButton'; import OpenDeleteModalButton from '@/components/yearperiods/OpenDeleteModalButton'; import OpenEditModalButton from '@/components/yearperiods/OpenEditModalButton'; -import { userAtom } from '@/store/atoms'; +import { useCurrentUser } from '@/store'; import { get } from '@api/api_methods'; import { Card, Title } from '@components/common'; import MainLayout from '@components/layout/MainLayout/MainLayout'; -import { YearPeriod, User } from '@type/common'; +import { User, YearPeriod } from '@type/common'; interface Props { yearPeriods: YearPeriod[]; @@ -32,7 +31,7 @@ export default function Periods(props: Props) { const { yearPeriods } = props; const router = useRouter(); - const user = useRecoilValue(userAtom); + const user = useCurrentUser(); const [currentUser, setCurrentUser] = useState(); const formatYearPeriods = diff --git a/view/next-project/src/store/atoms/AuthAtom.ts b/view/next-project/src/store/atoms/AuthAtom.ts deleted file mode 100644 index 6585db420..000000000 --- a/view/next-project/src/store/atoms/AuthAtom.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { atom } from 'recoil'; -import { recoilPersist } from 'recoil-persist'; - -import { STORE_KEYS } from '@/store/storeKeys'; - -const { persistAtom } = recoilPersist(); - -export const authAtom = atom({ - key: STORE_KEYS.AUTH_STATE, - default: { - isSignIn: false, - accessToken: '', - }, - effects_UNSTABLE: [persistAtom], -}); diff --git a/view/next-project/src/store/atoms/UserAtom.ts b/view/next-project/src/store/atoms/UserAtom.ts deleted file mode 100644 index 4199c1ba8..000000000 --- a/view/next-project/src/store/atoms/UserAtom.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { atom } from 'recoil'; -import { recoilPersist } from 'recoil-persist'; - -import { STORE_KEYS } from '@/store/storeKeys'; -import { User } from '@/type/common'; - -const { persistAtom } = recoilPersist(); - -export const userAtom = atom({ - key: STORE_KEYS.USER_STATE, - default: { - id: 0, - name: '', - bureauID: 0, - roleID: 0, - createdAt: '', - updatedAt: '', - }, - effects_UNSTABLE: [persistAtom], -}); diff --git a/view/next-project/src/store/atoms/index.ts b/view/next-project/src/store/atoms/index.ts deleted file mode 100644 index 1b5ce0d2c..000000000 --- a/view/next-project/src/store/atoms/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { authAtom } from './AuthAtom'; -import { userAtom } from './UserAtom'; - -export { authAtom, userAtom }; diff --git a/view/next-project/src/store/authStore.ts b/view/next-project/src/store/authStore.ts new file mode 100644 index 000000000..cc6f04e4e --- /dev/null +++ b/view/next-project/src/store/authStore.ts @@ -0,0 +1,39 @@ +import { create } from 'zustand'; +import { createJSONStorage, persist } from 'zustand/middleware'; + +interface AuthState { + isSignIn: boolean; + accessToken: string; + _hasHydrated: boolean; +} + +interface AuthActions { + setAuth: (auth: Pick) => void; + resetAuth: () => void; + setHasHydrated: (state: boolean) => void; +} + +const initialState: Pick = { + isSignIn: false, + accessToken: '', +}; + +export const useAuthStore = create()( + persist( + (set) => ({ + ...initialState, + _hasHydrated: false, + setAuth: (auth) => set(auth), + resetAuth: () => set(initialState), + setHasHydrated: (state) => set({ _hasHydrated: state }), + }), + { + name: 'auth-storage', + storage: createJSONStorage(() => localStorage), + partialize: (state) => ({ isSignIn: state.isSignIn, accessToken: state.accessToken }), + onRehydrateStorage: () => (state) => { + state?.setHasHydrated(true); + }, + }, + ), +); diff --git a/view/next-project/src/store/index.ts b/view/next-project/src/store/index.ts new file mode 100644 index 000000000..b07d9a72c --- /dev/null +++ b/view/next-project/src/store/index.ts @@ -0,0 +1,2 @@ +export { useAuthStore } from './authStore'; +export { useCurrentUser, useUserStore } from './userStore'; diff --git a/view/next-project/src/store/storeKeys.ts b/view/next-project/src/store/storeKeys.ts deleted file mode 100644 index 66f21473c..000000000 --- a/view/next-project/src/store/storeKeys.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const STORE_KEYS = { - AUTH_STATE: 'authAtom', - USER_STATE: 'userAtom', -} as const; diff --git a/view/next-project/src/store/userStore.ts b/view/next-project/src/store/userStore.ts new file mode 100644 index 000000000..fba51ef26 --- /dev/null +++ b/view/next-project/src/store/userStore.ts @@ -0,0 +1,46 @@ +import { create } from 'zustand'; +import { createJSONStorage, persist } from 'zustand/middleware'; + +import { User } from '@/type/common'; + +interface UserState { + user: User; + _hasHydrated: boolean; +} + +interface UserActions { + setUser: (user: User) => void; + resetUser: () => void; + setHasHydrated: (state: boolean) => void; +} + +const initialUser: User = { + id: 0, + name: '', + bureauID: 0, + roleID: 0, + createdAt: '', + updatedAt: '', +}; + +export const useUserStore = create()( + persist( + (set) => ({ + user: initialUser, + _hasHydrated: false, + setUser: (user) => set({ user }), + resetUser: () => set({ user: initialUser }), + setHasHydrated: (state) => set({ _hasHydrated: state }), + }), + { + name: 'user-storage', + storage: createJSONStorage(() => localStorage), + partialize: (state) => ({ user: state.user }), + onRehydrateStorage: () => (state) => { + state?.setHasHydrated(true); + }, + }, + ), +); + +export const useCurrentUser = () => useUserStore((state) => state.user); diff --git a/view/next-project/src/stories/auth/SignInView.stories.tsx b/view/next-project/src/stories/auth/SignInView.stories.tsx index 552a44a57..2d38d69d8 100644 --- a/view/next-project/src/stories/auth/SignInView.stories.tsx +++ b/view/next-project/src/stories/auth/SignInView.stories.tsx @@ -1,5 +1,3 @@ -import { RecoilRoot } from 'recoil'; - import { SignInView } from '@components/auth'; import type { Meta, StoryFn } from '@storybook/react'; @@ -7,13 +5,6 @@ import type { Meta, StoryFn } from '@storybook/react'; const meta: Meta = { title: 'FinanSu/auth/SignInView', component: SignInView, - decorators: [ - (Story) => ( - - - - ), - ], tags: ['autodocs'], argTypes: {}, }; diff --git a/view/next-project/src/stories/auth/SignUpView.stories.tsx b/view/next-project/src/stories/auth/SignUpView.stories.tsx index 390d2ec1e..1f8e6d1ce 100644 --- a/view/next-project/src/stories/auth/SignUpView.stories.tsx +++ b/view/next-project/src/stories/auth/SignUpView.stories.tsx @@ -1,5 +1,3 @@ -import { RecoilRoot } from 'recoil'; - import { SignUpView } from '@components/auth'; import type { Meta, StoryFn } from '@storybook/react'; @@ -7,13 +5,6 @@ import type { Meta, StoryFn } from '@storybook/react'; const meta: Meta = { title: 'FinanSu/auth/SignUpView', component: SignUpView, - decorators: [ - (Story) => ( - - - - ), - ], tags: ['autodocs'], argTypes: {}, }; diff --git a/view/next-project/src/stories/common/ChakraUIDropdown.stories.tsx b/view/next-project/src/stories/common/ChakraUIDropdown.stories.tsx index b249e9155..ae7d63363 100644 --- a/view/next-project/src/stories/common/ChakraUIDropdown.stories.tsx +++ b/view/next-project/src/stories/common/ChakraUIDropdown.stories.tsx @@ -1,5 +1,3 @@ -import { RecoilRoot } from 'recoil'; - import { ChakraUIDropdown } from '@components/common'; import type { Meta, StoryFn } from '@storybook/react'; @@ -7,13 +5,6 @@ import type { Meta, StoryFn } from '@storybook/react'; const meta: Meta = { title: 'FinanSu/common/ChakraUIDropdown', component: ChakraUIDropdown, - decorators: [ - (Story) => ( - - - - ), - ], tags: ['autodocs'], argTypes: {}, }; diff --git a/view/next-project/src/stories/common/Header.stories.tsx b/view/next-project/src/stories/common/Header.stories.tsx index 219acc01e..e712e916a 100644 --- a/view/next-project/src/stories/common/Header.stories.tsx +++ b/view/next-project/src/stories/common/Header.stories.tsx @@ -1,5 +1,3 @@ -import { RecoilRoot } from 'recoil'; - import { Header } from '@components/common'; import type { Meta, StoryFn } from '@storybook/react'; @@ -7,13 +5,6 @@ import type { Meta, StoryFn } from '@storybook/react'; const meta: Meta = { title: 'FinanSu/common/Header', component: Header, - decorators: [ - (Story) => ( - - - - ), - ], tags: ['autodocs'], argTypes: {}, }; diff --git a/view/next-project/src/stories/purchasereports/DetailModal.stories.tsx b/view/next-project/src/stories/purchasereports/DetailModal.stories.tsx index 3a877905c..035e3f0eb 100644 --- a/view/next-project/src/stories/purchasereports/DetailModal.stories.tsx +++ b/view/next-project/src/stories/purchasereports/DetailModal.stories.tsx @@ -1,21 +1,12 @@ -import { RecoilRoot } from 'recoil'; - import { DetailModal } from '@components/purchasereports/'; -import { PURCHASE_REPORT_VIEW, EXPENSES } from '../constants'; +import { EXPENSES, PURCHASE_REPORT_VIEW } from '../constants'; import type { Meta, StoryFn } from '@storybook/react'; const meta: Meta = { title: 'FinanSu/purchasereports/DetailModal', component: DetailModal, - decorators: [ - (Story) => ( - - - - ), - ], tags: ['autodocs'], argTypes: {}, }; diff --git a/view/next-project/src/stories/purchasereports/EditModal.stories.tsx b/view/next-project/src/stories/purchasereports/EditModal.stories.tsx index 511469c26..278ea4197 100644 --- a/view/next-project/src/stories/purchasereports/EditModal.stories.tsx +++ b/view/next-project/src/stories/purchasereports/EditModal.stories.tsx @@ -1,5 +1,3 @@ -import { RecoilRoot } from 'recoil'; - import { EditModal } from '@components/purchasereports'; import type { Meta, StoryFn } from '@storybook/react'; @@ -7,13 +5,6 @@ import type { Meta, StoryFn } from '@storybook/react'; const meta: Meta = { title: 'FinanSu/purchasereports/EditModal', component: EditModal, - decorators: [ - (Story) => ( - - - - ), - ], tags: ['autodocs'], argTypes: {}, }; diff --git a/view/next-project/src/stories/purchasereports/PurchaseReportAddModal.stories.tsx b/view/next-project/src/stories/purchasereports/PurchaseReportAddModal.stories.tsx index 20fad4105..d9c846e67 100644 --- a/view/next-project/src/stories/purchasereports/PurchaseReportAddModal.stories.tsx +++ b/view/next-project/src/stories/purchasereports/PurchaseReportAddModal.stories.tsx @@ -1,5 +1,3 @@ -import { RecoilRoot } from 'recoil'; - import { PurchaseReportAddModal } from '@components/purchasereports'; import type { Meta, StoryFn } from '@storybook/react'; @@ -7,13 +5,6 @@ import type { Meta, StoryFn } from '@storybook/react'; const meta: Meta = { title: 'FinanSu/purchasereports/PurchaseReportAddModal', component: PurchaseReportAddModal, - decorators: [ - (Story) => ( - - - - ), - ], tags: ['autodocs'], argTypes: {}, }; diff --git a/view/next-project/src/stories/purchasereports/PurchaseReportConfirmModal.stories.tsx b/view/next-project/src/stories/purchasereports/PurchaseReportConfirmModal.stories.tsx index 10ed65684..f531a0146 100644 --- a/view/next-project/src/stories/purchasereports/PurchaseReportConfirmModal.stories.tsx +++ b/view/next-project/src/stories/purchasereports/PurchaseReportConfirmModal.stories.tsx @@ -1,5 +1,3 @@ -import { RecoilRoot } from 'recoil'; - import { PurchaseReportConfirmModal } from '@components/purchasereports'; import { PURCHASE_ITEM } from '../constants'; @@ -9,13 +7,6 @@ import type { Meta, StoryFn } from '@storybook/react'; const meta: Meta = { title: 'FinanSu/purchasereports/PurchaseReportConfirmModal', component: PurchaseReportConfirmModal, - decorators: [ - (Story) => ( - - - - ), - ], tags: ['autodocs'], argTypes: {}, }; diff --git a/view/next-project/src/stories/purchasereports/PurchaseReportItemNumModal.stories.tsx b/view/next-project/src/stories/purchasereports/PurchaseReportItemNumModal.stories.tsx index 834557b95..4c5e604aa 100644 --- a/view/next-project/src/stories/purchasereports/PurchaseReportItemNumModal.stories.tsx +++ b/view/next-project/src/stories/purchasereports/PurchaseReportItemNumModal.stories.tsx @@ -1,5 +1,3 @@ -import { RecoilRoot } from 'recoil'; - import { PurchaseReportItemNumModal } from '@components/purchasereports'; import type { Meta } from '@storybook/react'; @@ -7,13 +5,6 @@ import type { Meta } from '@storybook/react'; const meta: Meta = { title: 'FinanSu/purchasereports/PurchaseReportItemNumModal', component: PurchaseReportItemNumModal, - decorators: [ - (Story) => ( - - - - ), - ], tags: ['autodocs'], argTypes: {}, }; diff --git a/view/next-project/src/stories/yearperiods/AddModal.stories.tsx b/view/next-project/src/stories/yearperiods/AddModal.stories.tsx index 8070bd4d3..c72bcdd39 100644 --- a/view/next-project/src/stories/yearperiods/AddModal.stories.tsx +++ b/view/next-project/src/stories/yearperiods/AddModal.stories.tsx @@ -1,6 +1,4 @@ // /Users/kobayashiryota/Workspace/FinanSu/view/next-project/src/stories/yearperiods/AddModal.stories.tsx -import { RecoilRoot } from 'recoil'; - import { AddModal } from '@components/yearperiods'; import type { Meta, StoryFn } from '@storybook/react'; @@ -8,13 +6,6 @@ import type { Meta, StoryFn } from '@storybook/react'; const meta: Meta = { title: 'FinanSu/yearperiods/AddModal', component: AddModal, - decorators: [ - (Story) => ( - - - - ), - ], argTypes: {}, tags: ['autodocs'], };