From abc49e3dda1c5e3cd095224980f5ba4c341762ea Mon Sep 17 00:00:00 2001 From: rito528 <39003544+rito528@users.noreply.github.com> Date: Sun, 14 Apr 2024 20:38:53 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20admin=E3=83=9A=E3=83=BC=E3=82=B8?= =?UTF-8?q?=E3=81=AE=E4=B8=80=E9=83=A8=E3=83=AC=E3=82=A4=E3=82=A2=E3=82=A6?= =?UTF-8?q?=E3=83=88=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 3 + .../admin/_components/AdminNavigationBar.tsx | 27 ++++-- .../admin/_components/DashboardMenu.tsx | 52 ++++++----- .../admin/forms/create/CreateForm2.tsx | 36 ++++++++ .../_react-hook-form-components/RHFProps.ts | 8 ++ .../RHFTextField.tsx | 35 +++++++ src/app/(authed)/admin/forms/create/page.tsx | 10 +- src/app/(authed)/admin/forms/page.tsx | 81 +++++++++++++++-- src/app/(authed)/admin/layout.tsx | 41 ++++----- src/app/(authed)/admin/page.tsx | 8 +- .../admin/theme/adminDashboardTheme.ts | 24 +++++ src/components/Dashboard.tsx | 32 +++---- src/features/form/components/CreateForm.tsx | 33 +++---- .../form/components/DashboardFormList.tsx | 91 ++++++++----------- yarn.lock | 19 ++++ 15 files changed, 346 insertions(+), 154 deletions(-) create mode 100644 src/app/(authed)/admin/forms/create/CreateForm2.tsx create mode 100644 src/app/(authed)/admin/forms/create/_react-hook-form-components/RHFProps.ts create mode 100644 src/app/(authed)/admin/forms/create/_react-hook-form-components/RHFTextField.tsx create mode 100644 src/app/(authed)/admin/theme/adminDashboardTheme.ts diff --git a/package.json b/package.json index 4f8e69e9..fec6b50c 100644 --- a/package.json +++ b/package.json @@ -17,11 +17,14 @@ "@azure/msal-browser": "^3.2.0", "@azure/msal-node": "^2.6.4", "@azure/msal-react": "^2.0.3", + "@emotion/cache": "^11.11.0", "@emotion/react": "^11.11.1", "@emotion/styled": "^11.11.0", + "@hookform/resolvers": "^3.3.4", "@material-ui/core": "^4.12.4", "@mui/icons-material": "^5.14.3", "@mui/material": "^5.14.4", + "@mui/material-nextjs": "^5.15.11", "@mui/x-data-grid": "^6.12.1", "browserslist": "^4.23.0", "dayjs": "^1.11.9", diff --git a/src/app/(authed)/admin/_components/AdminNavigationBar.tsx b/src/app/(authed)/admin/_components/AdminNavigationBar.tsx index 2c6aea22..26bb9bac 100644 --- a/src/app/(authed)/admin/_components/AdminNavigationBar.tsx +++ b/src/app/(authed)/admin/_components/AdminNavigationBar.tsx @@ -1,5 +1,6 @@ 'use client'; +import SearchIcon from '@mui/icons-material/Search'; import { Typography, Box, @@ -10,16 +11,21 @@ import { IconButton, InputBase, } from '@mui/material'; -import SearchIcon from '@mui/icons-material/Search'; import Image from 'next/image'; const SearchField = () => { return ( - + { theme.zIndex.drawer + 1 }} + sx={{ + zIndex: (theme) => theme.zIndex.drawer + 1, + backgroundColor: (theme) => theme.palette.secondary.main, + }} > {'seichi-portal - + Seichi Portal Admin diff --git a/src/app/(authed)/admin/_components/DashboardMenu.tsx b/src/app/(authed)/admin/_components/DashboardMenu.tsx index a0a1402f..6f95c07f 100644 --- a/src/app/(authed)/admin/_components/DashboardMenu.tsx +++ b/src/app/(authed)/admin/_components/DashboardMenu.tsx @@ -2,47 +2,57 @@ import { Star } from '@mui/icons-material'; import { - styled, Typography, MenuList, MenuItem, ListItemIcon, + Divider, } from '@mui/material'; import Drawer from '@mui/material/Drawer'; -const Demo = styled('div')(({ theme }) => ({ - backgroundColor: theme.palette.background.paper, - color: theme.palette.text.primary, -})); - const drawerWidth = 240; const DashboardMenu = () => { return ( Menu - - - {['Dashboard', 'Forms', 'Announcements'].map((value) => { - return ( - - - - - {value} - - ); - })} - - + + {['Dashboard', 'Forms', 'Announcements'].map((value) => { + return ( + + + + + {value} + + ); + })} + + ); }; diff --git a/src/app/(authed)/admin/forms/create/CreateForm2.tsx b/src/app/(authed)/admin/forms/create/CreateForm2.tsx new file mode 100644 index 00000000..5ae836f1 --- /dev/null +++ b/src/app/(authed)/admin/forms/create/CreateForm2.tsx @@ -0,0 +1,36 @@ +import { zodResolver } from '@hookform/resolvers/zod'; +import { Container, Stack, TextField, Typography } from '@mui/material'; +import { useForm } from 'react-hook-form'; +import { formSchema } from '@/_schemas/formSchema'; +import type { Form} from '@/_schemas/formSchema'; + +export const CreateFormComponent = () => { + const { + control, + handleSubmit, + register, + formState: { errors }, + } = useForm
({ + mode: 'onSubmit', + reValidateMode: 'onBlur', + resolver: zodResolver(formSchema), + }); + + const onSubmit = (data: Form) => { + // todo: データの送信処理を書く + }; + + return ( + + + フォームタイトル + + + + + ); +}; diff --git a/src/app/(authed)/admin/forms/create/_react-hook-form-components/RHFProps.ts b/src/app/(authed)/admin/forms/create/_react-hook-form-components/RHFProps.ts new file mode 100644 index 00000000..b87995ab --- /dev/null +++ b/src/app/(authed)/admin/forms/create/_react-hook-form-components/RHFProps.ts @@ -0,0 +1,8 @@ +import type { FieldValues, UseControllerProps } from 'react-hook-form'; + +export type RHFProps = Pick< + UseControllerProps, + 'name' | 'control' +> & { + label: string; +}; diff --git a/src/app/(authed)/admin/forms/create/_react-hook-form-components/RHFTextField.tsx b/src/app/(authed)/admin/forms/create/_react-hook-form-components/RHFTextField.tsx new file mode 100644 index 00000000..a9766f6b --- /dev/null +++ b/src/app/(authed)/admin/forms/create/_react-hook-form-components/RHFTextField.tsx @@ -0,0 +1,35 @@ +import { Stack, TextField, Typography } from '@mui/material'; +import { useController } from 'react-hook-form'; +import type { RHFProps } from './RHFProps'; +import type { FieldValues} from 'react-hook-form'; + +export const RHFTextField = ({ + name, + control, + label, +}: RHFProps) => { + const { + field, + formState: { errors }, + } = useController({ name, control }); + + const errorMessage = errors?.[name]?.message as string; + + return ( + + {label} + + {errorMessage && ( + + {errorMessage} + + )} + + ); +}; diff --git a/src/app/(authed)/admin/forms/create/page.tsx b/src/app/(authed)/admin/forms/create/page.tsx index 315087e2..78abbf25 100644 --- a/src/app/(authed)/admin/forms/create/page.tsx +++ b/src/app/(authed)/admin/forms/create/page.tsx @@ -1,11 +1,15 @@ 'use client'; -import { CreateFormComponent } from '@/features/form/components/CreateForm'; +import { CssBaseline, ThemeProvider } from '@mui/material'; +import { CreateFormComponent } from './CreateForm2'; +import adminDashboardTheme from '../../theme/adminDashboardTheme'; + const Home = () => { return ( - <> + + - + ); }; diff --git a/src/app/(authed)/admin/forms/page.tsx b/src/app/(authed)/admin/forms/page.tsx index 6b263579..9e750ee0 100644 --- a/src/app/(authed)/admin/forms/page.tsx +++ b/src/app/(authed)/admin/forms/page.tsx @@ -1,11 +1,18 @@ 'use client'; +import { Add } from '@mui/icons-material'; +import { + Autocomplete, + Box, + Button, + Chip, + Grid, + Stack, + TextField, +} from '@mui/material'; import { redirect } from 'next/navigation'; import useSWR from 'swr'; -import { - CreateFormButton, - Forms, -} from '@/features/form/components/DashboardFormList'; +import { Forms } from '@/features/form/components/DashboardFormList'; import type { MinimumForm } from '@/_schemas/formSchema'; const Home = () => { @@ -22,10 +29,68 @@ const Home = () => { } return ( - <> - - - + + + + option} + defaultValue={['Open']} + renderTags={(value: readonly string[], getTagProps) => + value.map((option: string, index: number) => ( + + )) + } + renderOption={(props, option) => { + return ( + + {option} + + ); + }} + renderInput={(params) => ( + // @ts-expect-error (解決方法がよくわからないのでとりあえずignoreする) + // FIXME: あとで調べる + + )} + /> + + + + + + + + + + + ); }; diff --git a/src/app/(authed)/admin/layout.tsx b/src/app/(authed)/admin/layout.tsx index 8948e99e..26efc21c 100644 --- a/src/app/(authed)/admin/layout.tsx +++ b/src/app/(authed)/admin/layout.tsx @@ -1,36 +1,35 @@ +'use client'; + import '../../globals.css'; +import { ThemeProvider } from '@emotion/react'; +import { CssBaseline } from '@mui/material'; +import { AppRouterCacheProvider } from '@mui/material-nextjs/v13-appRouter'; import { Inter } from 'next/font/google'; -import { AuthenticatedTemplate } from '@/features/user/components/AuthenticatedTemplate'; import { MsalProvider } from '@/features/user/components/MsalProvider'; -import { NeedToSignin } from '@/features/user/components/NeedToSignin'; -import { UnauthenticatedTemplate } from '@/features/user/components/UnauthenticatedTemplate'; -import styles from '../../page.module.css'; -import type { Metadata } from 'next'; -import type { ReactNode } from 'react'; import AdminNavigationBar from './_components/AdminNavigationBar'; import DashboardMenu from './_components/DashboardMenu'; +import adminDashboardTheme from './theme/adminDashboardTheme'; +import styles from '../../page.module.css'; +import type { ReactNode } from 'react'; const inter = Inter({ subsets: ['latin'] }); -export const metadata: Metadata = { - title: 'Seichi Portal', - description: '整地鯖公式のポータルサイトです。', -}; - const RootLayout = ({ children }: { children: ReactNode }) => { return ( -
- - - - {children} - - - - -
+ + + + + +
+ + {children} +
+
+
+
); diff --git a/src/app/(authed)/admin/page.tsx b/src/app/(authed)/admin/page.tsx index 3413fef2..76431d96 100644 --- a/src/app/(authed)/admin/page.tsx +++ b/src/app/(authed)/admin/page.tsx @@ -4,7 +4,6 @@ import { redirect } from 'next/navigation'; import useSWR from 'swr'; import DataTable from '@/components/Dashboard'; import type { BatchAnswer } from '@/_schemas/formSchema'; -import DashboardMenu from './_components/DashboardMenu'; const Home = () => { const fetcher = (url: string) => fetch(url).then((res) => res.json()); @@ -19,12 +18,7 @@ const Home = () => { return null; } - return ( - <> - - - - ); + return ; }; export default Home; diff --git a/src/app/(authed)/admin/theme/adminDashboardTheme.ts b/src/app/(authed)/admin/theme/adminDashboardTheme.ts new file mode 100644 index 00000000..88f64e8b --- /dev/null +++ b/src/app/(authed)/admin/theme/adminDashboardTheme.ts @@ -0,0 +1,24 @@ +'use client'; + +import { createTheme } from '@mui/material'; + +const theme = createTheme({ + palette: { + primary: { + main: '#90CAF9', + contrastText: '#000000', + }, + secondary: { + main: '#001F38', + }, + background: { + default: '#010020', + }, + text: { + primary: '#FFFFFF', + }, + divider: '#001F38', + }, +}); + +export default theme; diff --git a/src/components/Dashboard.tsx b/src/components/Dashboard.tsx index 57be1753..e20eb429 100644 --- a/src/components/Dashboard.tsx +++ b/src/components/Dashboard.tsx @@ -34,23 +34,21 @@ const prepareRows = (answers: Answers) => { const DataTable = (answers: Answers) => { return ( -
- -
+ ); }; diff --git a/src/features/form/components/CreateForm.tsx b/src/features/form/components/CreateForm.tsx index 1ca92c90..2b55e14b 100644 --- a/src/features/form/components/CreateForm.tsx +++ b/src/features/form/components/CreateForm.tsx @@ -2,7 +2,6 @@ import Button from '@material-ui/core/Button'; import FormControlLabel from '@material-ui/core/FormControlLabel'; -import FormGroup from '@material-ui/core/FormGroup'; import IconButton from '@material-ui/core/IconButton'; import InputLabel from '@material-ui/core/InputLabel'; import Add from '@mui/icons-material/Add'; @@ -161,24 +160,22 @@ export const CreateFormComponent = () => { }; return ( - + {errors.root &&

{errors.root.message}

} - - - - - - + + + +
); }; diff --git a/src/features/form/components/DashboardFormList.tsx b/src/features/form/components/DashboardFormList.tsx index d4c458a3..dd67d530 100644 --- a/src/features/form/components/DashboardFormList.tsx +++ b/src/features/form/components/DashboardFormList.tsx @@ -1,34 +1,22 @@ 'use client'; -import Add from '@mui/icons-material/Add'; -import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; import Card from '@mui/material/Card'; import CardActions from '@mui/material/CardActions'; import CardContent from '@mui/material/CardContent'; import Grid from '@mui/material/Grid'; -import Paper from '@mui/material/Paper'; -import { styled } from '@mui/material/styles'; import Typography from '@mui/material/Typography'; import * as React from 'react'; import { formatString } from '../../../components/DateFormatter'; import type { MinimumForm } from '@/_schemas/formSchema'; -const Item = styled(Paper)(({ theme }) => ({ - backgroundColor: theme.palette.mode === 'dark' ? '#1A2027' : '#fff', - ...theme.typography.body2, - padding: theme.spacing(1), - textAlign: 'center', - color: theme.palette.text.secondary, -})); - interface Props { forms: MinimumForm[]; } const formatResponsePeriod = (startAt: string | null, endAt: string | null) => { if (startAt != null && endAt != null) { - return `回答可能期間: ${formatString(startAt)} ~ ${formatString(endAt)}`; + return `${formatString(startAt)} ~ ${formatString(endAt)}`; } else { return `回答期限なし`; } @@ -36,44 +24,43 @@ const formatResponsePeriod = (startAt: string | null, endAt: string | null) => { export const Forms = ({ forms }: Props) => { return ( - - - {forms.map((form, index) => { - return ( - - - - - - {form.title} - - - {formatResponsePeriod( - form.response_period.start_at, - form.response_period.end_at - )} - - - - - - - - - - ); - })} - - - ); -}; - -export const CreateFormButton = () => { - return ( - + + {forms.map((form, index) => { + return ( + + + + + {form.title} + + + {formatResponsePeriod( + form.response_period.start_at, + form.response_period.end_at + )} + + + + + + + + + ); + })} + ); }; diff --git a/yarn.lock b/yarn.lock index de8edb29..a42f286a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -89,6 +89,13 @@ dependencies: regenerator-runtime "^0.14.0" +"@babel/runtime@^7.23.9": + version "7.24.4" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.4.tgz#de795accd698007a66ba44add6cc86542aff1edd" + integrity sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/types@^7.22.15": version "7.22.19" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.19.tgz#7425343253556916e440e662bb221a93ddb75684" @@ -269,6 +276,11 @@ resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.1.3.tgz#6ee493102b45d796d69f1f472d4bf64e5244500a" integrity sha512-uvnFKtPgzLnpzzTRfhDlvXX0kLYi9lDRQbcDmT8iXl71Rx+uwSuaUIQl3DNC7w5OweAQ7XQMDObML+KaYDQfng== +"@hookform/resolvers@^3.3.4": + version "3.3.4" + resolved "https://registry.yarnpkg.com/@hookform/resolvers/-/resolvers-3.3.4.tgz#de9b668c2835eb06892290192de6e2a5c906229b" + integrity sha512-o5cgpGOuJYrd+iMKvkttOclgwRW86EsWJZZRC23prf0uU2i48Htq4PuT73AVb9ionFyZrwYEITuOFGF+BydEtQ== + "@humanwhocodes/config-array@^0.11.10": version "0.11.11" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.11.tgz#88a04c570dbbc7dd943e4712429c3df09bc32844" @@ -389,6 +401,13 @@ dependencies: "@babel/runtime" "^7.22.15" +"@mui/material-nextjs@^5.15.11": + version "5.15.11" + resolved "https://registry.yarnpkg.com/@mui/material-nextjs/-/material-nextjs-5.15.11.tgz#bf75eece88fb088e74eb5f0eef01f9f64f8ec7f4" + integrity sha512-cp5RWYbBngyi7NKP91R9QITllfxumCVPFjqe4AKzNROVuCot0VpgkafxXqfbv0uFsyUU0ROs0O2M3r17q604Aw== + dependencies: + "@babel/runtime" "^7.23.9" + "@mui/material@^5.14.4": version "5.14.9" resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.14.9.tgz#d536505a3728441cfe8003443f143ae87457767b"