timetable-app

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit b3c11af2a4ca04ee8b62c07ffcc1fa1d06e1696e
parent d0cbf7d7890269fbe92dee4005a267fa831543c5
Author: Yuukin256 <52195426+Yuukin256@users.noreply.github.com>
Date:   Fri, 16 Feb 2024 23:29:30 +0900

Merge pull request #14 from Yuukin256/maintenance-2024

20240216 2023年度サービス終了のためリダイレクトを設定
Diffstat:
Mnext.config.js | 32+++++++-------------------------
Dsrc/pages/[class]/index.tsx | 215-------------------------------------------------------------------------------
Dsrc/pages/api/auth.ts | 7-------
Dsrc/pages/api/trpc/[trpc].ts | 11-----------
4 files changed, 7 insertions(+), 258 deletions(-)

diff --git a/next.config.js b/next.config.js @@ -13,31 +13,13 @@ const nextConfig = withPWA({ swcPlugins: [['next-superjson-plugin', {}]], }, redirects: async () => { - const startClassRedirects = [1, 2, 3].flatMap((g) => - [1, 2, 3, 4, 5, 6, 7, 8].map((c) => { - const className = `${g}-${c}`; - return { - source: '/start', - has: [ - { - type: 'cookie', - key: 'default_class', - value: className, - }, - ], - destination: '/' + className, - permanent: false, - }; - }) - ); - - const startDefaultRedirect = { - source: '/start', - destination: '/', - permanent: false, - }; - - return [...startClassRedirects, startDefaultRedirect]; + return [ + { + source: '/:slug(.+)', + destination: '/', + permanent: false, + }, + ]; }, }); diff --git a/src/pages/[class]/index.tsx b/src/pages/[class]/index.tsx @@ -1,215 +0,0 @@ -import { Box } from '@mui/material'; -import { addDays, subDays } from 'date-fns'; - -import Head from 'client/components/Head'; -import Layout from 'client/components/Layout'; -import SlideFooter from 'client/components/SlideFooter'; -import DailyView from 'client/features/daily'; -import { CLASSES, DATE_FORMAT, GRADES } from 'common/constant'; -import formatDate from 'common/utils/formatDate'; -import { zodClass, zodGrade, zodPeriod } from 'common/utils/zod'; -import { caller } from 'server/routers/_app'; -import { prisma } from 'server/utils/prisma'; - -import type { DayTimetable, LessonData } from 'client/features/daily/types'; -import type { Class, Grade } from 'common/types'; -import type { NextPage, GetStaticPaths, GetStaticProps } from 'next'; - -type UrlQuery = { - class: string; -}; - -type PageProps = { - grade: Grade; - class: Class; - calendar: [string, DayTimetable][]; -}; - -export const getStaticPaths: GetStaticPaths<UrlQuery> = async () => { - const paths = GRADES.flatMap((g) => - CLASSES.map((c) => ({ params: { class: `${g}-${c}` } })) - ); - - return { - paths, - fallback: false, - }; -}; - -export const getStaticProps: GetStaticProps<PageProps, UrlQuery> = async ({ - params, -}) => { - if (typeof params === 'undefined') { - return { - notFound: true, - }; - } - - // 学年、クラスの数字が問題ないか確認 - const [gradeNumRaw, classNumRaw] = params.class - .split('-') - .map((v) => parseInt(v)); - const gradeParsed = zodGrade.safeParse(gradeNumRaw); - const classParsed = zodClass.safeParse(classNumRaw); - if (!gradeParsed.success || !classParsed.success) { - return { - notFound: true, - }; - } - - const now = new Date(); - const startDate = subDays(now, 5); - const endDate = addDays(now, 30); - - // クラスの標準時間割表を取得 - const { standard, id: classId } = await caller.classStandard.byGradeAndClass({ - grade: gradeParsed.data, - class: classParsed.data, - }); - - // 学年の曜日時限時間割表を取得 - const schedules = await prisma.gradeSchedule.findMany({ - where: { - grade: gradeParsed.data, - date: { - gte: startDate, - lte: endDate, - }, - }, - }); - - // クラスの臨時変更を取得 - const temporarySchedules = await prisma.classTemporarySchedule.findMany({ - where: { - classId, - date: { - gte: startDate, - lte: endDate, - }, - }, - }); - - // 学年のイベントを取得 - const events = await prisma.schoolEvent.findMany({ - where: { - grades: { has: gradeParsed.data }, - date: { - gte: startDate, - lte: endDate, - }, - }, - }); - - // 時間割を構築していくMap - const calendar: Map<string, DayTimetable> = new Map(); - - // イベントでループしてMapに当てはめていく - events.forEach((event) => { - const dateString = formatDate(event.date, DATE_FORMAT); - const old = calendar.get(dateString); - if (old) { - calendar.set(dateString, { - ...old, - events: [...old.events, event], - }); - } else { - calendar.set(dateString, { - date: event.date, - events: [event], - }); - } - }); - - // 曜日時限時間割表の1コマずつでループしてMapに当てはめていく - schedules.forEach(({ date, period, dayPeriod, updatedAt }) => { - const dateString = formatDate(date, DATE_FORMAT); - const parsedPeriod = zodPeriod.safeParse(period); - - if (!parsedPeriod.success) return; - - const old = calendar.get(dateString); - const dataToAdd: LessonData = { - subjects: standard.get(dayPeriod) ?? [], - basis: { type: 'dayPeriod', dayPeriod }, - updated: updatedAt, - }; - - if (old) { - calendar.set(dateString, { - ...old, - [parsedPeriod.data]: dataToAdd, - }); - } else { - calendar.set(dateString, { - date: date, - events: [], - [parsedPeriod.data]: dataToAdd, - }); - } - }); - - // 臨時変更を1コマずつループしてMapに当てはめていく - temporarySchedules.forEach(({ id, date, period, subjects, updatedAt }) => { - const dateString = formatDate(date, DATE_FORMAT); - const parsedPeriod = zodPeriod.safeParse(period); - - if (!parsedPeriod.success) return; - - const old = calendar.get(dateString); - const dataToAdd: LessonData = { - subjects, - basis: { type: 'temporary', id }, - updated: updatedAt, - }; - - if (old) { - calendar.set(dateString, { - ...old, - [parsedPeriod.data]: dataToAdd, - }); - } else { - calendar.set(dateString, { - date: date, - events: [], - [parsedPeriod.data]: dataToAdd, - }); - } - }); - - // 日付順に並び替えた配列にする - const sorted = [...calendar.entries()].sort((a, b) => { - return a[1].date.getTime() - b[1].date.getTime(); - }); - - return { - props: { - grade: gradeParsed.data, - class: classParsed.data, - calendar: sorted, - }, - revalidate: 60 * 60, // 1時間毎の訪問で再生成 - }; -}; - -// 5日前~30日後の時間割を表示するページ -const Page: NextPage<PageProps> = (props) => { - const className = `${props.grade}年${props.class}組`; - return ( - <> - <Head title={className} /> - <Layout title={className} footer={<SlideFooter />}> - <Box width={1}> - {props.calendar.length ? ( - <DailyView {...props} /> - ) : ( - <Box px={2}> - <p>データがありません</p> - </Box> - )} - </Box> - </Layout> - </> - ); -}; - -export default Page; diff --git a/src/pages/api/auth.ts b/src/pages/api/auth.ts @@ -1,7 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from 'next'; - -export default function handler(_: NextApiRequest, res: NextApiResponse) { - res.setHeader('WWW-authenticate', 'Basic realm="Secure Area"'); - res.statusCode = 401; - res.end(`Auth Required.`); -} diff --git a/src/pages/api/trpc/[trpc].ts b/src/pages/api/trpc/[trpc].ts @@ -1,11 +0,0 @@ -import * as trpcNext from '@trpc/server/adapters/next'; - -import { createContext } from 'server/context'; -import { appRouter } from 'server/routers/_app'; - -// export API handler -// @see https://trpc.io/docs/api-handler -export default trpcNext.createNextApiHandler({ - router: appRouter, - createContext, -});