, any> | never => useAspidaSWR({
9 | $get: option => handleAuthError(dispatch, fallback, api.get, option),
10 | $path: api.$path,
11 | } as API, ...option);
12 |
13 | export default useFetch;
14 |
--------------------------------------------------------------------------------
/apps/admin/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 | ///
4 |
--------------------------------------------------------------------------------
/apps/admin/next.config.js:
--------------------------------------------------------------------------------
1 | const withTranspileModules = require('next-transpile-modules')([
2 | '@fullcalendar',
3 | '@frourio-demo',
4 | ]);
5 | const withBundleAnalyzer = require('@next/bundle-analyzer')({
6 | enabled: process.env.ANALYZE === 'true',
7 | });
8 |
9 | module.exports = withBundleAnalyzer(withTranspileModules({
10 | webpack5: false,
11 | assetPrefix: process.env.CI ? '/frourio-demo/admin' : '',
12 | env: {
13 | FRONT_URL: process.env.FRONT_URL,
14 | LOCK_URL: process.env.LOCK_URL,
15 | },
16 | async rewrites() {
17 | return [
18 | {
19 | source: '/:any*',
20 | destination: '/',
21 | },
22 | ];
23 | },
24 | webpack: (config) => {
25 | // remove unused packages
26 | // https://github.com/mbrn/material-table/issues/2164#issuecomment-692525181
27 | config.module.rules.push({
28 | test: /jspdf|moment/, // material-table => jspdf, chart.js => moment
29 | loader: 'null-loader',
30 | });
31 |
32 | return config;
33 | },
34 | }));
--------------------------------------------------------------------------------
/apps/admin/pages/_app.tsx:
--------------------------------------------------------------------------------
1 | import type { FC, PropsWithChildren } from 'react';
2 | import type { AppProps } from 'next/app';
3 | import CssBaseline from '@material-ui/core/CssBaseline';
4 | import Head from '~/components/Head';
5 | import { StoreContextProvider } from '~/store';
6 |
7 | const SafeHydrate: FC = ({ children }: PropsWithChildren<{}>) => {
8 | return (
9 |
10 | {typeof window === 'undefined' ? null : children}
11 |
12 | );
13 | };
14 |
15 | const MyApp = ({ Component, pageProps }: PropsWithChildren) =>
16 |
17 |
18 |
19 |
20 |
21 | ;
22 |
23 | export default MyApp;
24 |
--------------------------------------------------------------------------------
/apps/admin/pages/_document.tsx:
--------------------------------------------------------------------------------
1 | import NextDocument, { Html, Head, Main, NextScript } from 'next/document';
2 |
3 | export default class Document extends NextDocument {
4 | render() {
5 | return
6 |
7 |
8 |
9 |
10 |
11 | ;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/apps/admin/public/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/technote-space/frourio-demo/8a0e82e8347375953cc9c857ed8777331f50afdb/apps/admin/public/favicon.png
--------------------------------------------------------------------------------
/apps/admin/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": [
4 | "next-env.d.ts",
5 | "**/*.ts",
6 | "**/*.tsx"
7 | ],
8 | "exclude": [
9 | "node_modules"
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/apps/admin/types/html.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.html' {
2 | const content: string;
3 | export default content;
4 | }
5 |
--------------------------------------------------------------------------------
/apps/admin/utils/license.ts:
--------------------------------------------------------------------------------
1 | import { processLicenses } from '@frourio-demo/utils/license';
2 | import licenses from '../license.json';
3 |
4 | export const getLicenseList = () => processLicenses(licenses);
5 |
--------------------------------------------------------------------------------
/apps/front/.gitignore:
--------------------------------------------------------------------------------
1 | *.pem
2 |
--------------------------------------------------------------------------------
/apps/front/_pages/contact.tsx:
--------------------------------------------------------------------------------
1 | import type { FC } from 'react';
2 | import { memo } from 'react';
3 | import { Box, Heading, Icon } from '@chakra-ui/react';
4 | import { IoMdConstruct } from 'react-icons/io';
5 |
6 | const Contact: FC = memo(() => {
7 | return
8 | お問い合わせ
9 | 工事中...
10 | ;
11 | });
12 |
13 | Contact.displayName = 'Contact';
14 | export default Contact;
15 |
--------------------------------------------------------------------------------
/apps/front/_pages/facility.tsx:
--------------------------------------------------------------------------------
1 | import type { FC } from 'react';
2 | import { memo } from 'react';
3 | import { Box, Image } from '@chakra-ui/react';
4 |
5 | const Facility: FC = memo(() => {
6 | return
7 |
8 |
9 |
10 |
11 | ;
12 | });
13 |
14 | Facility.displayName = 'Facility';
15 | export default Facility;
16 |
--------------------------------------------------------------------------------
/apps/front/_pages/info.tsx:
--------------------------------------------------------------------------------
1 | import type { FC } from 'react';
2 | import { memo } from 'react';
3 | import { Box, Heading, Icon } from '@chakra-ui/react';
4 | import { IoMdConstruct } from 'react-icons/io';
5 |
6 | const Info: FC = memo(() => {
7 | return
8 | お知らせ
9 | 工事中...
10 | ;
11 | });
12 |
13 | Info.displayName = 'Info';
14 | export default Info;
15 |
--------------------------------------------------------------------------------
/apps/front/_pages/meal.tsx:
--------------------------------------------------------------------------------
1 | import type { FC } from 'react';
2 | import { memo } from 'react';
3 | import { Box, Image } from '@chakra-ui/react';
4 |
5 | const Meal: FC = memo(() => {
6 | return
7 |
8 |
9 |
10 |
11 | ;
12 | });
13 |
14 | Meal.displayName = 'Meal';
15 | export default Meal;
16 |
--------------------------------------------------------------------------------
/apps/front/_pages/privacy.tsx:
--------------------------------------------------------------------------------
1 | import type { FC } from 'react';
2 | import { memo } from 'react';
3 | import { Box, Heading, Icon } from '@chakra-ui/react';
4 | import { IoMdConstruct } from 'react-icons/io';
5 |
6 | const Privacy: FC = memo(() => {
7 | return
8 | プライバシーポリシー
9 | 工事中...
10 | ;
11 | });
12 |
13 | Privacy.displayName = 'Privacy';
14 | export default Privacy;
15 |
--------------------------------------------------------------------------------
/apps/front/_pages/terms.tsx:
--------------------------------------------------------------------------------
1 | import type { FC } from 'react';
2 | import { memo } from 'react';
3 | import { Box, Heading, Icon } from '@chakra-ui/react';
4 | import { IoMdConstruct } from 'react-icons/io';
5 |
6 | const Terms: FC = memo(() => {
7 | return
8 | 利用規約
9 | 工事中...
10 | ;
11 | });
12 |
13 | Terms.displayName = 'Terms';
14 | export default Terms;
15 |
--------------------------------------------------------------------------------
/apps/front/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ['next/babel'],
3 | overrides: [{
4 | include: [
5 | /@fullcalendar/,
6 | /@frourio-demo/,
7 | /slick-carousel/,
8 | ],
9 | plugins: [
10 | ['babel-plugin-transform-require-ignore', {
11 | extensions: ['.css'],
12 | }],
13 | ],
14 | }],
15 | };
16 |
--------------------------------------------------------------------------------
/apps/front/components/Layout/Footer.tsx:
--------------------------------------------------------------------------------
1 | import type { FC } from 'react';
2 | import { memo } from 'react';
3 | import { Link as RouterLink } from 'react-router-dom';
4 | import { Flex, Box, Text, Link } from '@chakra-ui/react';
5 |
6 | const Footer: FC = memo(() => {
7 | return
8 |
9 | 利用規約
10 | プライバシーポリシー
11 | お問い合わせ
12 |
13 |
14 |
15 | {(new Date()).getFullYear()} — Frourioの宿
16 |
17 |
18 | ;
19 | });
20 |
21 | Footer.displayName = 'Footer';
22 | export default Footer;
23 |
--------------------------------------------------------------------------------
/apps/front/components/Layout/index.tsx:
--------------------------------------------------------------------------------
1 | import type { ReactNode } from 'react';
2 | import { memo } from 'react';
3 | import Header from './Header';
4 | import Footer from './Footer';
5 | import { Flex } from '@chakra-ui/react';
6 |
7 | type Props = {
8 | children?: ReactNode
9 | }
10 |
11 | const Layout = memo(({ children }: Props) =>
18 |
19 |
20 |
21 | {children}
22 |
23 |
24 |
25 | );
26 |
27 | Layout.displayName = 'Layout';
28 | export default Layout;
29 |
--------------------------------------------------------------------------------
/apps/front/components/MiniCalendar.tsx:
--------------------------------------------------------------------------------
1 | import type { FC } from 'react';
2 | import { memo } from 'react';
3 | import { Box } from '@chakra-ui/react';
4 | import { getDayOfWeek, getWeekColor, getYear, getMonth, getDate } from '^/utils/date';
5 |
6 | type Props = {
7 | date: Date;
8 | }
9 |
10 | const MiniCalendar: FC = memo(({ date }: Props) => {
11 | return
12 | {getYear(date)}
13 |
14 | {getMonth(date)}
15 | {getDate(date)}
16 | ({getDayOfWeek(date)})
17 |
18 | ;
19 | });
20 |
21 | MiniCalendar.displayName = 'MiniCalendar';
22 | export default MiniCalendar;
23 |
--------------------------------------------------------------------------------
/apps/front/components/ToastWrapper.tsx:
--------------------------------------------------------------------------------
1 | import type { FC } from 'react';
2 | import { useEffect } from 'react';
3 | import { useToast } from '@chakra-ui/react';
4 | import { useDispatchContext, useStoreContext } from '^/store';
5 | import { clearNotices } from '^/utils/actions';
6 |
7 | const ToastWrapper: FC = () => {
8 | const { notices } = useStoreContext();
9 | const { dispatch } = useDispatchContext();
10 | const toast = useToast();
11 |
12 | useEffect(() => {
13 | if (notices.length) {
14 | clearNotices(dispatch);
15 | notices.filter(notice => notice.title || notice.description).forEach(notice => {
16 | toast({
17 | ...notice,
18 | duration: 5000,
19 | isClosable: true,
20 | });
21 | });
22 | }
23 | }, [notices.length]);
24 |
25 | return null;
26 | };
27 |
28 | export default ToastWrapper;
29 |
--------------------------------------------------------------------------------
/apps/front/components/account/SwitchTab.tsx:
--------------------------------------------------------------------------------
1 | import type { FC } from 'react';
2 | import { memo, useCallback } from 'react';
3 | import { TabList, Tab } from '@chakra-ui/react';
4 | import { useAuth0 } from '@auth0/auth0-react';
5 | import { Link as RouterLink } from 'react-router-dom';
6 |
7 | const SwitchTab: FC = memo(() => {
8 | const { logout } = useAuth0();
9 | const handleLogout = useCallback(() => {
10 | logout({
11 | returnTo: `${window.location.origin}${process.env.BASE_PATH}`,
12 | });
13 | }, []);
14 |
15 | return
16 | 会員情報
17 | 予約一覧
18 | ログアウト
19 | ;
20 | });
21 |
22 | SwitchTab.displayName = 'SwitchTab';
23 | export default SwitchTab;
24 |
--------------------------------------------------------------------------------
/apps/front/hooks/useFetch.ts:
--------------------------------------------------------------------------------
1 | import type { Dispatch, SwrApiOptions, SwrApiResponse, SwrApiType, SwrFallbackType } from '@frourio-demo/types';
2 | import type { SWRResponse } from 'swr';
3 | import useAspidaSWR from '@aspida/swr';
4 | import { handleAuthError } from '^/utils/api';
5 | import type { MaybeUndefined } from '@frourio-demo/types';
6 |
7 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
8 | const useFetch = , F extends SwrFallbackType>(dispatch: Dispatch, fallback: F, api: API, ...option: SwrApiOptions): SwrApiResponse | SWRResponse, any> | never => useAspidaSWR({
9 | $get: option => handleAuthError(dispatch, fallback, api.get, option),
10 | $path: api.$path,
11 | } as API, ...option);
12 |
13 | export default useFetch;
14 |
--------------------------------------------------------------------------------
/apps/front/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 | ///
4 |
--------------------------------------------------------------------------------
/apps/front/next.config.js:
--------------------------------------------------------------------------------
1 | const withTranspileModules = require('next-transpile-modules')([
2 | '@fullcalendar',
3 | '@frourio-demo',
4 | 'slick-carousel',
5 | ]);
6 | const withBundleAnalyzer = require('@next/bundle-analyzer')({
7 | enabled: process.env.ANALYZE === 'true',
8 | });
9 |
10 | module.exports = withBundleAnalyzer(withTranspileModules({
11 | webpack5: false,
12 | assetPrefix: process.env.CI ? '/frourio-demo/front' : '',
13 | env: {
14 | BASE_PATH: process.env.CI ? '/frourio-demo/front' : ''
15 | },
16 | async rewrites() {
17 | return [
18 | {
19 | source: '/:any*',
20 | destination: '/',
21 | },
22 | ];
23 | },
24 | }));
--------------------------------------------------------------------------------
/apps/front/pages/_document.tsx:
--------------------------------------------------------------------------------
1 | import { ColorModeScript } from '@chakra-ui/react';
2 | import NextDocument, { Html, Head, Main, NextScript } from 'next/document';
3 |
4 | export default class Document extends NextDocument {
5 | render() {
6 | return
7 |
8 |
9 |
10 |
11 |
12 |
13 | ;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/apps/front/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import { memo } from 'react';
2 | import { Switch, Route, Redirect } from 'react-router-dom';
3 | import Auth from '^/components/Auth';
4 | import Layout from '^/components/Layout';
5 | import ToastWrapper from '^/components/ToastWrapper';
6 | import pages from '^/_pages';
7 |
8 | const Index = memo(() => <>
9 |
10 |
11 |
12 |
13 | {Object.keys(pages).map(page =>
14 | ,
20 | )}
21 |
22 |
23 |
24 | >);
25 |
26 | Index.displayName = 'Index';
27 | export default Index;
28 |
--------------------------------------------------------------------------------
/apps/front/public/cover1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/technote-space/frourio-demo/8a0e82e8347375953cc9c857ed8777331f50afdb/apps/front/public/cover1.jpg
--------------------------------------------------------------------------------
/apps/front/public/cover2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/technote-space/frourio-demo/8a0e82e8347375953cc9c857ed8777331f50afdb/apps/front/public/cover2.jpg
--------------------------------------------------------------------------------
/apps/front/public/cover3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/technote-space/frourio-demo/8a0e82e8347375953cc9c857ed8777331f50afdb/apps/front/public/cover3.jpg
--------------------------------------------------------------------------------
/apps/front/public/cover4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/technote-space/frourio-demo/8a0e82e8347375953cc9c857ed8777331f50afdb/apps/front/public/cover4.jpg
--------------------------------------------------------------------------------
/apps/front/public/cover5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/technote-space/frourio-demo/8a0e82e8347375953cc9c857ed8777331f50afdb/apps/front/public/cover5.jpg
--------------------------------------------------------------------------------
/apps/front/public/cover6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/technote-space/frourio-demo/8a0e82e8347375953cc9c857ed8777331f50afdb/apps/front/public/cover6.jpg
--------------------------------------------------------------------------------
/apps/front/public/facility1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/technote-space/frourio-demo/8a0e82e8347375953cc9c857ed8777331f50afdb/apps/front/public/facility1.jpg
--------------------------------------------------------------------------------
/apps/front/public/facility2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/technote-space/frourio-demo/8a0e82e8347375953cc9c857ed8777331f50afdb/apps/front/public/facility2.jpg
--------------------------------------------------------------------------------
/apps/front/public/facility3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/technote-space/frourio-demo/8a0e82e8347375953cc9c857ed8777331f50afdb/apps/front/public/facility3.jpg
--------------------------------------------------------------------------------
/apps/front/public/facility4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/technote-space/frourio-demo/8a0e82e8347375953cc9c857ed8777331f50afdb/apps/front/public/facility4.jpg
--------------------------------------------------------------------------------
/apps/front/public/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/technote-space/frourio-demo/8a0e82e8347375953cc9c857ed8777331f50afdb/apps/front/public/favicon.png
--------------------------------------------------------------------------------
/apps/front/public/meal1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/technote-space/frourio-demo/8a0e82e8347375953cc9c857ed8777331f50afdb/apps/front/public/meal1.jpg
--------------------------------------------------------------------------------
/apps/front/public/meal2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/technote-space/frourio-demo/8a0e82e8347375953cc9c857ed8777331f50afdb/apps/front/public/meal2.jpg
--------------------------------------------------------------------------------
/apps/front/public/meal3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/technote-space/frourio-demo/8a0e82e8347375953cc9c857ed8777331f50afdb/apps/front/public/meal3.jpg
--------------------------------------------------------------------------------
/apps/front/public/meal4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/technote-space/frourio-demo/8a0e82e8347375953cc9c857ed8777331f50afdb/apps/front/public/meal4.jpg
--------------------------------------------------------------------------------
/apps/front/server.ts:
--------------------------------------------------------------------------------
1 | import https from 'https';
2 | import fs from 'fs';
3 | import next from 'next';
4 |
5 | if (fs.existsSync('localhost.frourio-demo.com-key.pem') && fs.existsSync('localhost.frourio-demo.com.pem')) {
6 | const hostname = process.env.HOST || 'localhost.frourio-demo.com';
7 | const port = parseInt(process.env.PORT || '3000', 10);
8 | const dev = process.env.NODE_ENV !== 'production';
9 | const app = next({ dev, dir: __dirname });
10 | const handle = app.getRequestHandler();
11 | const options = {
12 | key: fs.readFileSync('localhost.frourio-demo.com-key.pem'),
13 | cert: fs.readFileSync('localhost.frourio-demo.com.pem'),
14 | };
15 |
16 | app.prepare().then(() => {
17 | https.createServer(options, handle).listen(port, hostname, () => {
18 | console.log(`Server running at https://${hostname}:${port}/`);
19 | });
20 | });
21 | } else {
22 | console.log('The cert files are required.', 'Please run `yarn setup:front` command to prepare them.');
23 | }
24 |
--------------------------------------------------------------------------------
/apps/front/stripe.json:
--------------------------------------------------------------------------------
1 | {
2 | "publicKey": "pk_test_51IGcm8DBz4ItKNrD4B3d5KYOPUxaslYfhgiGLXAJQSft8Y7InVvYIMfieW1oUYO3hW5K4Z7RLQDxsXto9m27gPOY00VOPO2l9h"
3 | }
--------------------------------------------------------------------------------
/apps/front/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": [
4 | "next-env.d.ts",
5 | "**/*.ts",
6 | "**/*.tsx"
7 | ],
8 | "exclude": [
9 | "node_modules",
10 | "server.ts"
11 | ],
12 | "ts-node": {
13 | "compilerOptions": {
14 | "module": "commonjs",
15 | "isolatedModules": false
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/apps/front/types/html.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.html' {
2 | const content: string;
3 | export default content;
4 | }
5 |
--------------------------------------------------------------------------------
/apps/front/types/index.ts:
--------------------------------------------------------------------------------
1 | import type { FC } from 'react';
2 | import type { Guest } from '$/packages/domain/database/guest';
3 |
4 | export type NoticeType = {
5 | title: string,
6 | description: string,
7 | status: 'error' | 'info' | 'success' | 'warning',
8 | }
9 | export type ContextState = {
10 | guest?: Partial;
11 | title?: string;
12 | notices: NoticeType[];
13 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
14 | localStorage?: Record;
15 | onRemoveToken: boolean;
16 | onRefreshToken: boolean;
17 | };
18 |
19 | export type Page = {
20 | path?: string;
21 | exact?: boolean;
22 | label: string;
23 | page: FC;
24 | };
25 |
26 | export type Address = {
27 | 'prefecture_jis_code': string;
28 | 'city_jis_code': string;
29 | 'zip_code': string;
30 | 'prefecture_name_kana': string;
31 | 'city_name_kana': string;
32 | 'town_name_kana': string;
33 | 'prefecture_name': string;
34 | 'city_name': string;
35 | 'town_name': string;
36 | }
37 |
--------------------------------------------------------------------------------
/apps/front/utils/history.ts:
--------------------------------------------------------------------------------
1 | import { createBrowserHistory, createMemoryHistory } from 'history';
2 |
3 | const history = typeof window === 'undefined' ? createMemoryHistory() : createBrowserHistory();
4 | export default history;
5 |
--------------------------------------------------------------------------------
/apps/lock/_pages/index.tsx:
--------------------------------------------------------------------------------
1 | import type { Page } from '#/types';
2 | import Top from './top';
3 | import Rooms from './rooms';
4 | import Room from './room';
5 |
6 | const pages: Record = {
7 | top: {
8 | path: '/',
9 | exact: true,
10 | label: 'トップ',
11 | page: Top,
12 | },
13 | rooms: {
14 | label: 'お部屋',
15 | page: Rooms,
16 | },
17 | room: {
18 | path: '/room/:id',
19 | label: 'お部屋',
20 | page: Room,
21 | },
22 | };
23 |
24 | export type PageKeys = keyof typeof pages;
25 | export default pages;
26 |
--------------------------------------------------------------------------------
/apps/lock/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ['next/babel'],
3 | overrides: [{
4 | include: [
5 | /@frourio-demo/,
6 | ],
7 | }],
8 | };
9 |
--------------------------------------------------------------------------------
/apps/lock/components/Layout/index.tsx:
--------------------------------------------------------------------------------
1 | import type { ReactNode } from 'react';
2 | import { memo } from 'react';
3 | import { Flex } from '@chakra-ui/react';
4 |
5 | type Props = {
6 | children?: ReactNode
7 | }
8 |
9 | const Layout = memo(({ children }: Props) =>
16 |
17 |
18 | {children}
19 |
20 |
21 | );
22 |
23 | Layout.displayName = 'Layout';
24 | export default Layout;
25 |
--------------------------------------------------------------------------------
/apps/lock/components/Reservation.tsx:
--------------------------------------------------------------------------------
1 | import type { FC } from 'react';
2 | import type { RoomWithValidReservation } from '$/packages/application/usecase/lock/rooms/find';
3 | import { memo } from 'react';
4 | import { format } from 'date-fns';
5 | import { Box, Heading } from '@chakra-ui/react';
6 |
7 | type Props = {
8 | room: Required;
9 | }
10 |
11 | const Reservation: FC = memo(({ room }: Props) => {
12 | return
13 | {room.reservation.guestName}様
14 | {room.reservation.status === 'reserved' &&
15 | チェックイン予定:{format(new Date(room.reservation.checkin), 'yyyy/MM/dd HH:mm')}
16 | }
17 | ;
18 | });
19 |
20 | Reservation.displayName = 'Reservation';
21 | export default Reservation;
22 |
--------------------------------------------------------------------------------
/apps/lock/components/ToastWrapper.tsx:
--------------------------------------------------------------------------------
1 | import type { FC } from 'react';
2 | import { useEffect } from 'react';
3 | import { useToast } from '@chakra-ui/react';
4 | import { useDispatchContext, useStoreContext } from '#/store';
5 | import { clearNotices } from '#/utils/actions';
6 |
7 | const ToastWrapper: FC = () => {
8 | const { notices } = useStoreContext();
9 | const { dispatch } = useDispatchContext();
10 | const toast = useToast();
11 |
12 | useEffect(() => {
13 | if (notices.length) {
14 | clearNotices(dispatch);
15 | notices.filter(notice => notice.title || notice.description).forEach(notice => {
16 | toast({
17 | ...notice,
18 | duration: 5000,
19 | isClosable: true,
20 | });
21 | });
22 | }
23 | }, [notices.length]);
24 |
25 | return null;
26 | };
27 |
28 | export default ToastWrapper;
29 |
--------------------------------------------------------------------------------
/apps/lock/hooks/useFetch.ts:
--------------------------------------------------------------------------------
1 | import type { Dispatch, SwrApiOptions, SwrApiResponse, SwrApiType, SwrFallbackType } from '@frourio-demo/types';
2 | import type { SWRResponse } from 'swr';
3 | import useAspidaSWR from '@aspida/swr';
4 | import { handleAuthError } from '#/utils/api';
5 | import type { MaybeUndefined } from '@frourio-demo/types';
6 |
7 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
8 | const useFetch = , F extends SwrFallbackType>(dispatch: Dispatch, fallback: F, api: API, ...option: SwrApiOptions): SwrApiResponse | SWRResponse, any> | never => useAspidaSWR({
9 | $get: option => handleAuthError(dispatch, fallback, api.get, option),
10 | $path: api.$path,
11 | } as API, ...option);
12 |
13 | export default useFetch;
14 |
--------------------------------------------------------------------------------
/apps/lock/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 | ///
4 |
--------------------------------------------------------------------------------
/apps/lock/next.config.js:
--------------------------------------------------------------------------------
1 | const withTranspileModules = require('next-transpile-modules')([
2 | '@frourio-demo',
3 | ]);
4 | const withBundleAnalyzer = require('@next/bundle-analyzer')({
5 | enabled: process.env.ANALYZE === 'true',
6 | });
7 |
8 | module.exports = withBundleAnalyzer(withTranspileModules({
9 | webpack5: false,
10 | assetPrefix: process.env.CI ? '/frourio-demo/lock' : '',
11 | env: {
12 | BASE_PATH: process.env.CI ? '/frourio-demo/lock' : '',
13 | },
14 | async rewrites() {
15 | return [
16 | {
17 | source: '/:any*',
18 | destination: '/',
19 | },
20 | ];
21 | },
22 | }));
--------------------------------------------------------------------------------
/apps/lock/pages/_document.tsx:
--------------------------------------------------------------------------------
1 | import { ColorModeScript } from '@chakra-ui/react';
2 | import NextDocument, { Html, Head, Main, NextScript } from 'next/document';
3 |
4 | export default class Document extends NextDocument {
5 | render() {
6 | return
7 |
8 |
9 |
10 |
11 |
12 |
13 | ;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/apps/lock/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import { memo } from 'react';
2 | import { Switch, Route, Redirect } from 'react-router-dom';
3 | import Layout from '#/components/Layout';
4 | import ToastWrapper from '#/components/ToastWrapper';
5 | import pages from '#/_pages';
6 |
7 | const Index = memo(() => <>
8 |
9 |
10 |
11 | {Object.keys(pages).map(page =>
12 | ,
18 | )}
19 |
20 |
21 |
22 | >);
23 |
24 | Index.displayName = 'Index';
25 | export default Index;
26 |
--------------------------------------------------------------------------------
/apps/lock/public/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/technote-space/frourio-demo/8a0e82e8347375953cc9c857ed8777331f50afdb/apps/lock/public/favicon.png
--------------------------------------------------------------------------------
/apps/lock/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": [
4 | "next-env.d.ts",
5 | "**/*.ts",
6 | "**/*.tsx"
7 | ],
8 | "exclude": [
9 | "node_modules"
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/apps/lock/types/html.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.html' {
2 | const content: string;
3 | export default content;
4 | }
5 |
--------------------------------------------------------------------------------
/apps/lock/types/index.ts:
--------------------------------------------------------------------------------
1 | import type { FC } from 'react';
2 |
3 | export type NoticeType = {
4 | title: string,
5 | description: string,
6 | status: 'error' | 'info' | 'success' | 'warning',
7 | }
8 | export type ContextState = {
9 | title?: string;
10 | notices: NoticeType[];
11 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
12 | localStorage?: Record;
13 | };
14 |
15 | export type Page = {
16 | path?: string;
17 | exact?: boolean;
18 | label: string;
19 | page: FC;
20 | };
21 |
--------------------------------------------------------------------------------
/apps/lock/utils/actions.ts:
--------------------------------------------------------------------------------
1 | import type { Dispatch } from '@frourio-demo/types';
2 |
3 | export const changeTitle = (dispatch: Dispatch, title?: string) => dispatch({ type: 'TITLE', title });
4 |
5 | export const setNotice = (dispatch: Dispatch, description: string, title?: string): void => dispatch({
6 | type: 'SET_NOTICE',
7 | title,
8 | description,
9 | });
10 | export const setError = (dispatch: Dispatch, description: string, title?: string): void => dispatch({
11 | type: 'SET_ERROR',
12 | title,
13 | description,
14 | });
15 | export const clearNotices = (dispatch: Dispatch): void => dispatch({ type: 'CLEAR_NOTICES' });
16 |
--------------------------------------------------------------------------------
/apps/lock/utils/history.ts:
--------------------------------------------------------------------------------
1 | import { createBrowserHistory, createMemoryHistory } from 'history';
2 |
3 | const history = typeof window === 'undefined' ? createMemoryHistory() : createBrowserHistory();
4 | export default history;
5 |
--------------------------------------------------------------------------------
/apps/server/.env.example:
--------------------------------------------------------------------------------
1 | SERVER_PORT=8080
2 | BASE_PATH=/api
3 | API_ORIGIN=http://localhost
4 | JWT_SECRET=supersecret
5 | SMTP_HOST=localhost
6 | SMTP_PORT=1025
7 | SMTP_SECURE=
8 | SMTP_USER=hogehoge@gmail.com
9 | SMTP_PASS=
10 | SMTP_FROM=
11 | SMTP_BCC=
12 | FRONT_URL=https://localhost.frourio-demo.com:3001
13 | CRYPTO_PASS=xTIqSbNNQqvXrpugmx1EUd2i5+9sAkXD69/nVj5L+lo=
14 | CRYPTO_SALT=OueA7B4N4om5+ahex3PYWA==
15 | CRYPTO_ALGO=aes-256-cbc
16 | STRIPE_SECRET=
17 | STRIPE_WEBHOOK_SECRET=
18 | SLACK_WEBHOOK_URL=
19 |
--------------------------------------------------------------------------------
/apps/server/.gitignore:
--------------------------------------------------------------------------------
1 | **/$*.ts
2 | **/*.db
3 | index.js*
4 | /tasks/
5 | database.json
6 | .upload
7 | public/icons/*
8 | !public/icons/dummy.svg
9 | *.pid
10 | /logs/
11 |
--------------------------------------------------------------------------------
/apps/server/__tests__/__mocks__/Cancelled.ts:
--------------------------------------------------------------------------------
1 | export default 'Cancelled';
--------------------------------------------------------------------------------
/apps/server/__tests__/__mocks__/Footer.ts:
--------------------------------------------------------------------------------
1 | export default 'Footer';
--------------------------------------------------------------------------------
/apps/server/__tests__/__mocks__/Head.ts:
--------------------------------------------------------------------------------
1 | export default 'Head';
--------------------------------------------------------------------------------
/apps/server/__tests__/__mocks__/Header.ts:
--------------------------------------------------------------------------------
1 | export default 'Header';
--------------------------------------------------------------------------------
/apps/server/__tests__/__mocks__/Paid.ts:
--------------------------------------------------------------------------------
1 | export default 'Paid';
--------------------------------------------------------------------------------
/apps/server/__tests__/__mocks__/Reserved.ts:
--------------------------------------------------------------------------------
1 | export default 'Reserved';
--------------------------------------------------------------------------------
/apps/server/__tests__/__mocks__/RoomKey.ts:
--------------------------------------------------------------------------------
1 | export default 'RoomKey';
--------------------------------------------------------------------------------
/apps/server/__tests__/__mocks__/infra/database/factory.ts:
--------------------------------------------------------------------------------
1 | /* istanbul ignore file */
2 |
3 | /* eslint-disable @typescript-eslint/no-explicit-any */
4 | import type { DelegateTypes } from '$/packages/infra/database/prisma/runner';
5 | import Faker from 'faker';
6 | import { runner } from '$/packages/infra/database/prisma/runner';
7 |
8 | export const getDummyData = (type: DelegateTypes, override?: Partial, ...params: any[]) => ({
9 | ...runner.getDefine(type)(Faker, params),
10 | ...override,
11 | });
12 |
13 | export const dropObjectData = >(data: T) => {
14 | Object.keys(data).forEach(key => {
15 | if (data[key] && typeof data[key] === 'object' && !(data[key] instanceof Date)) {
16 | delete data[key];
17 | }
18 | });
19 | return data;
20 | };
21 |
22 | export const filterCreateData = (data: T): T & { createdAt: Date; updatedAt: Date; } => ({
23 | ...dropObjectData(data),
24 | createdAt: new Date(),
25 | updatedAt: new Date(),
26 | });
27 |
--------------------------------------------------------------------------------
/apps/server/__tests__/__mocks__/infra/database/role.ts:
--------------------------------------------------------------------------------
1 | /* istanbul ignore file */
2 |
3 | import type {
4 | IRoleRepository,
5 | Role,
6 | } from '$/packages/domain/database/role';
7 |
8 | export class TestRoleRepository implements IRoleRepository {
9 | public constructor(private store: Role[] = []) {
10 | }
11 |
12 | count(): Promise {
13 | return Promise.resolve(this.store.length);
14 | }
15 |
16 | list(): Promise {
17 | return Promise.resolve(this.store);
18 | }
19 | }
20 |
21 | export const roleRepository = new TestRoleRepository();
22 |
--------------------------------------------------------------------------------
/apps/server/__tests__/packages/application/service/pages.test.ts:
--------------------------------------------------------------------------------
1 | import { getSkip, getCurrentPage } from '$/packages/application/service/pages';
2 |
3 | describe('getSkip', () => {
4 | it('should get skip number', () => {
5 | expect(getSkip(10, 2)).toBe(20);
6 | });
7 | });
8 |
9 | describe('getCurrentPage', () => {
10 | it('should get current page number', () => {
11 | expect(getCurrentPage(10, 31, 3)).toBe(3);
12 | expect(getCurrentPage(10, 30, 3)).toBe(2);
13 | expect(getCurrentPage(10, 29, 3)).toBe(2);
14 | expect(getCurrentPage(10, 21, 3)).toBe(2);
15 | expect(getCurrentPage(10, 20, 3)).toBe(1);
16 | expect(getCurrentPage(10, 19, 3)).toBe(1);
17 | expect(getCurrentPage(10, 10, 3)).toBe(0);
18 | expect(getCurrentPage(10, 0, 3)).toBe(0);
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/apps/server/__tests__/packages/application/usecase/admin/admins/create.test.ts:
--------------------------------------------------------------------------------
1 | import { CreateAdminUseCase } from '$/packages/application/usecase/admin/admins/create';
2 | import { TestAdminRepository } from '$/__tests__/__mocks__/infra/database/admin';
3 | import { ResponseRepository } from '$/packages/infra/http/response';
4 |
5 | describe('CreateAdminUseCase', () => {
6 | it('should create admin', async() => {
7 | expect(await (new CreateAdminUseCase(new TestAdminRepository(), new ResponseRepository())).execute({
8 | name: 'test',
9 | email: 'test@example.com',
10 | password: 'pass',
11 | })).toEqual({
12 | status: 201,
13 | body: {
14 | id: 1,
15 | email: expect.any(String),
16 | name: expect.any(String),
17 | password: expect.any(String),
18 | createdAt: expect.any(Date),
19 | updatedAt: expect.any(Date),
20 | },
21 | });
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/apps/server/__tests__/packages/application/usecase/admin/admins/delete.test.ts:
--------------------------------------------------------------------------------
1 | import { DeleteAdminUseCase } from '$/packages/application/usecase/admin/admins/delete';
2 | import { TestAdminRepository, getDummyAdminData } from '$/__tests__/__mocks__/infra/database/admin';
3 | import { ResponseRepository } from '$/packages/infra/http/response';
4 |
5 | describe('DeleteAdminUseCase', () => {
6 | it('should delete admin', async() => {
7 | expect(await (new DeleteAdminUseCase(new TestAdminRepository([getDummyAdminData()]), new ResponseRepository())).execute(1)).toEqual({
8 | status: 200,
9 | body: {
10 | id: 1,
11 | email: expect.any(String),
12 | name: expect.any(String),
13 | password: expect.any(String),
14 | createdAt: expect.any(Date),
15 | updatedAt: expect.any(Date),
16 | },
17 | });
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/apps/server/__tests__/packages/application/usecase/admin/admins/find.test.ts:
--------------------------------------------------------------------------------
1 | import { FindAdminUseCase } from '$/packages/application/usecase/admin/admins/find';
2 | import { TestAdminRepository, getDummyAdminData } from '$/__tests__/__mocks__/infra/database/admin';
3 | import { ResponseRepository } from '$/packages/infra/http/response';
4 |
5 | describe('FindAdminUseCase', () => {
6 | it('should find admin', async() => {
7 | expect(await (new FindAdminUseCase(new TestAdminRepository([getDummyAdminData()]), new ResponseRepository())).execute(1)).toEqual({
8 | status: 200,
9 | body: {
10 | id: 1,
11 | email: expect.any(String),
12 | name: expect.any(String),
13 | password: expect.any(String),
14 | createdAt: expect.any(Date),
15 | updatedAt: expect.any(Date),
16 | },
17 | });
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/apps/server/__tests__/packages/application/usecase/admin/admins/listRoles.test.ts:
--------------------------------------------------------------------------------
1 | import { ListRolesUseCase } from '$/packages/application/usecase/admin/admins/listRoles';
2 | import { TestRoleRepository } from '$/__tests__/__mocks__/infra/database/role';
3 | import { ResponseRepository } from '$/packages/infra/http/response';
4 |
5 | describe('ListRolesUseCase', () => {
6 | it('should list roles', async() => {
7 | expect(await (new ListRolesUseCase(new TestRoleRepository([
8 | { role: 'a', name: 'A' },
9 | { role: 'b', name: 'B' },
10 | { role: 'c', name: 'C' },
11 | ]), new ResponseRepository())).execute()).toEqual({
12 | status: 200,
13 | body: { a: 'A', b: 'B', c: 'C' },
14 | });
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/apps/server/__tests__/packages/application/usecase/admin/admins/searchRole.test.ts:
--------------------------------------------------------------------------------
1 | import { SearchRoleUseCase } from '$/packages/application/usecase/admin/admins/searchRole';
2 | import { TestRoleRepository } from '$/__tests__/__mocks__/infra/database/role';
3 | import { ResponseRepository } from '$/packages/infra/http/response';
4 | import { getQuery } from '$/__tests__/utils';
5 |
6 | describe('SearchRoleUseCase', () => {
7 | it('should list roles', async() => {
8 | expect(await (new SearchRoleUseCase(new TestRoleRepository([
9 | { role: 'a', name: 'A' },
10 | { role: 'b', name: 'B' },
11 | { role: 'c', name: 'C' },
12 | ]), new ResponseRepository())).execute(getQuery({
13 | orderBy: 'name',
14 | }))).toEqual({
15 | status: 200,
16 | body: {
17 | page: 0,
18 | totalCount: 3,
19 | data: [
20 | { role: 'a', name: 'A' },
21 | { role: 'b', name: 'B' },
22 | { role: 'c', name: 'C' },
23 | ],
24 | },
25 | });
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/apps/server/__tests__/packages/application/usecase/admin/admins/update.test.ts:
--------------------------------------------------------------------------------
1 | import { UpdateAdminUseCase } from '$/packages/application/usecase/admin/admins/update';
2 | import { TestAdminRepository, getDummyAdminData } from '$/__tests__/__mocks__/infra/database/admin';
3 | import { ResponseRepository } from '$/packages/infra/http/response';
4 |
5 | describe('UpdateAdminUseCase', () => {
6 | it('should update admin', async() => {
7 | expect(await (new UpdateAdminUseCase(new TestAdminRepository([getDummyAdminData()]), new ResponseRepository())).execute(1, {
8 | name: 'new name',
9 | email: 'new@example.com',
10 | password: 'pass',
11 | })).toEqual({
12 | status: 200,
13 | body: {
14 | id: 1,
15 | email: 'new@example.com',
16 | name: 'new name',
17 | password: expect.any(String),
18 | createdAt: expect.any(Date),
19 | updatedAt: expect.any(Date),
20 | },
21 | });
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/apps/server/__tests__/packages/application/usecase/admin/dashboard/getSelectableRooms.test.ts:
--------------------------------------------------------------------------------
1 | import { GetSelectableRoomsUseCase } from '$/packages/application/usecase/admin/dashboard/getSelectableRooms';
2 | import { TestRoomRepository, getDummyRoomData } from '$/__tests__/__mocks__/infra/database/room';
3 | import { ResponseRepository } from '$/packages/infra/http/response';
4 |
5 | describe('GetSelectableRoomsUseCase', () => {
6 | it('should get selectable rooms', async() => {
7 | expect(await (new GetSelectableRoomsUseCase(new TestRoomRepository([getDummyRoomData()]), new ResponseRepository())).execute()).toEqual({
8 | status: 200,
9 | body: [{
10 | id: 1,
11 | name: expect.any(String),
12 | }],
13 | });
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/apps/server/__tests__/packages/application/usecase/admin/guests/create.test.ts:
--------------------------------------------------------------------------------
1 | import { CreateGuestUseCase } from '$/packages/application/usecase/admin/guests/create';
2 | import { TestGuestRepository } from '$/__tests__/__mocks__/infra/database/guest';
3 | import { ResponseRepository } from '$/packages/infra/http/response';
4 |
5 | describe('CreateGuestUseCase', () => {
6 | it('should create guest', async() => {
7 | expect(await (new CreateGuestUseCase(new TestGuestRepository(), new ResponseRepository())).execute({
8 | email: 'test@example.com',
9 | })).toEqual({
10 | status: 201,
11 | body: {
12 | id: 1,
13 | email: 'test@example.com',
14 | createdAt: expect.any(Date),
15 | updatedAt: expect.any(Date),
16 | },
17 | });
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/apps/server/__tests__/packages/application/usecase/admin/guests/delete.test.ts:
--------------------------------------------------------------------------------
1 | import { DeleteGuestUseCase } from '$/packages/application/usecase/admin/guests/delete';
2 | import { TestGuestRepository, getDummyGuestData } from '$/__tests__/__mocks__/infra/database/guest';
3 | import { ResponseRepository } from '$/packages/infra/http/response';
4 |
5 | describe('DeleteGuestUseCase', () => {
6 | it('should delete guest', async() => {
7 | expect(await (new DeleteGuestUseCase(new TestGuestRepository([getDummyGuestData()]), new ResponseRepository())).execute(1)).toEqual({
8 | status: 200,
9 | body: {
10 | id: 1,
11 | email: expect.any(String),
12 | name: expect.any(String),
13 | nameKana: expect.any(String),
14 | phone: expect.any(String),
15 | zipCode: expect.any(String),
16 | address: expect.any(String),
17 | createdAt: expect.any(Date),
18 | updatedAt: expect.any(Date),
19 | },
20 | });
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/apps/server/__tests__/packages/application/usecase/admin/guests/find.test.ts:
--------------------------------------------------------------------------------
1 | import { FindGuestUseCase } from '$/packages/application/usecase/admin/guests/find';
2 | import { TestGuestRepository, getDummyGuestData } from '$/__tests__/__mocks__/infra/database/guest';
3 | import { ResponseRepository } from '$/packages/infra/http/response';
4 |
5 | describe('FindGuestUseCase', () => {
6 | it('should find guest', async() => {
7 | expect(await (new FindGuestUseCase(new TestGuestRepository([getDummyGuestData()]), new ResponseRepository())).execute(1)).toEqual({
8 | status: 200,
9 | body: {
10 | id: 1,
11 | email: expect.any(String),
12 | name: expect.any(String),
13 | nameKana: expect.any(String),
14 | phone: expect.any(String),
15 | zipCode: expect.any(String),
16 | address: expect.any(String),
17 | createdAt: expect.any(Date),
18 | updatedAt: expect.any(Date),
19 | },
20 | });
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/apps/server/__tests__/packages/application/usecase/admin/reservations/getSelectedGuest.test.ts:
--------------------------------------------------------------------------------
1 | import { GetSelectedGuestUseCase } from '$/packages/application/usecase/admin/reservations/getSelectedGuest';
2 | import { TestGuestRepository, getDummyGuestData } from '$/__tests__/__mocks__/infra/database/guest';
3 | import { ResponseRepository } from '$/packages/infra/http/response';
4 |
5 | describe('GetSelectedGuestUseCase', () => {
6 | it('should get selected guest', async() => {
7 | expect(await (new GetSelectedGuestUseCase(new TestGuestRepository([getDummyGuestData()]), new ResponseRepository())).execute(1)).toEqual({
8 | status: 200,
9 | body: {
10 | id: 1,
11 | email: expect.any(String),
12 | name: expect.any(String),
13 | },
14 | });
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/apps/server/__tests__/packages/application/usecase/admin/reservations/getSelectedRoom.test.ts:
--------------------------------------------------------------------------------
1 | import { GetSelectedRoomUseCase } from '$/packages/application/usecase/admin/reservations/getSelectedRoom';
2 | import { TestRoomRepository, getDummyRoomData } from '$/__tests__/__mocks__/infra/database/room';
3 | import { ResponseRepository } from '$/packages/infra/http/response';
4 |
5 | describe('GetSelectedRoomUseCase', () => {
6 | it('should get selected room', async() => {
7 | expect(await (new GetSelectedRoomUseCase(new TestRoomRepository([getDummyRoomData()]), new ResponseRepository())).execute(1)).toEqual({
8 | status: 200,
9 | body: {
10 | id: 1,
11 | name: expect.any(String),
12 | number: expect.any(Number),
13 | price: expect.any(Number),
14 | },
15 | });
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/apps/server/__tests__/packages/application/usecase/admin/rooms/create.test.ts:
--------------------------------------------------------------------------------
1 | import { CreateRoomUseCase } from '$/packages/application/usecase/admin/rooms/create';
2 | import { TestRoomRepository } from '$/__tests__/__mocks__/infra/database/room';
3 | import { ResponseRepository } from '$/packages/infra/http/response';
4 |
5 | describe('CreateRoomUseCase', () => {
6 | it('should create room', async() => {
7 | expect(await (new CreateRoomUseCase(new TestRoomRepository(), new ResponseRepository())).execute({
8 | name: 'test',
9 | number: 2,
10 | price: 3000,
11 | })).toEqual({
12 | status: 201,
13 | body: {
14 | id: 1,
15 | name: 'test',
16 | number: 2,
17 | price: 3000,
18 | createdAt: expect.any(Date),
19 | updatedAt: expect.any(Date),
20 | },
21 | });
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/apps/server/__tests__/packages/application/usecase/admin/rooms/delete.test.ts:
--------------------------------------------------------------------------------
1 | import { DeleteRoomUseCase } from '$/packages/application/usecase/admin/rooms/delete';
2 | import { TestRoomRepository, getDummyRoomData } from '$/__tests__/__mocks__/infra/database/room';
3 | import { ResponseRepository } from '$/packages/infra/http/response';
4 |
5 | describe('DeleteRoomUseCase', () => {
6 | it('should delete room', async() => {
7 | expect(await (new DeleteRoomUseCase(new TestRoomRepository([getDummyRoomData()]), new ResponseRepository())).execute(1)).toEqual({
8 | status: 200,
9 | body: {
10 | id: 1,
11 | name: expect.any(String),
12 | number: expect.any(Number),
13 | price: expect.any(Number),
14 | createdAt: expect.any(Date),
15 | updatedAt: expect.any(Date),
16 | },
17 | });
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/apps/server/__tests__/packages/application/usecase/admin/rooms/find.test.ts:
--------------------------------------------------------------------------------
1 | import { FindRoomUseCase } from '$/packages/application/usecase/admin/rooms/find';
2 | import { TestRoomRepository, getDummyRoomData } from '$/__tests__/__mocks__/infra/database/room';
3 | import { ResponseRepository } from '$/packages/infra/http/response';
4 |
5 | describe('FindRoomUseCase', () => {
6 | it('should find room', async() => {
7 | expect(await (new FindRoomUseCase(new TestRoomRepository([getDummyRoomData()]), new ResponseRepository())).execute(1)).toEqual({
8 | status: 200,
9 | body: {
10 | id: 1,
11 | name: expect.any(String),
12 | number: expect.any(Number),
13 | price: expect.any(Number),
14 | createdAt: expect.any(Date),
15 | updatedAt: expect.any(Date),
16 | },
17 | });
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/apps/server/__tests__/packages/application/usecase/admin/rooms/update.test.ts:
--------------------------------------------------------------------------------
1 | import { UpdateRoomUseCase } from '$/packages/application/usecase/admin/rooms/update';
2 | import { TestRoomRepository, getDummyRoomData } from '$/__tests__/__mocks__/infra/database/room';
3 | import { ResponseRepository } from '$/packages/infra/http/response';
4 |
5 | describe('UpdateRoomUseCase', () => {
6 | it('should update room', async() => {
7 | expect(await (new UpdateRoomUseCase(new TestRoomRepository([getDummyRoomData()]), new ResponseRepository())).execute(1, {
8 | name: 'new name',
9 | })).toEqual({
10 | status: 200,
11 | body: {
12 | id: 1,
13 | name: 'new name',
14 | number: expect.any(Number),
15 | price: expect.any(Number),
16 | createdAt: expect.any(Date),
17 | updatedAt: expect.any(Date),
18 | },
19 | });
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/apps/server/__tests__/packages/application/usecase/front/account/getGuestInfo.test.ts:
--------------------------------------------------------------------------------
1 | import { GetGuestInfoUseCase } from '$/packages/application/usecase/front/account/getGuestInfo';
2 | import { TestGuestRepository, getDummyGuestData } from '$/__tests__/__mocks__/infra/database/guest';
3 | import { ResponseRepository } from '$/packages/infra/http/response';
4 |
5 | describe('GetGuestInfoUseCase', () => {
6 | it('should get guest info', async() => {
7 | expect(await (new GetGuestInfoUseCase(new TestGuestRepository([getDummyGuestData()]), new ResponseRepository())).execute({ id: 1 })).toEqual({
8 | status: 200,
9 | body: {
10 | id: 1,
11 | email: expect.any(String),
12 | name: expect.any(String),
13 | nameKana: expect.any(String),
14 | phone: expect.any(String),
15 | zipCode: expect.any(String),
16 | address: expect.any(String),
17 | createdAt: expect.any(Date),
18 | updatedAt: expect.any(Date),
19 | },
20 | });
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/apps/server/__tests__/packages/application/usecase/front/guest/find.ts:
--------------------------------------------------------------------------------
1 | import { FindGuestUseCase } from '$/packages/application/usecase/front/guest/find';
2 | import { TestGuestRepository, getDummyGuestData } from '$/__tests__/__mocks__/infra/database/guest';
3 | import { ResponseRepository } from '$/packages/infra/http/response';
4 |
5 | describe('FindGuestUseCase', () => {
6 | it('should get guest info', async() => {
7 | expect(await (new FindGuestUseCase(new TestGuestRepository([getDummyGuestData()]), new ResponseRepository())).execute({ id: 1 })).toEqual({
8 | status: 200,
9 | body: {
10 | id: 1,
11 | email: expect.any(String),
12 | name: expect.any(String),
13 | nameKana: expect.any(String),
14 | phone: expect.any(String),
15 | zipCode: expect.any(String),
16 | address: expect.any(String),
17 | createdAt: expect.any(Date),
18 | updatedAt: expect.any(Date),
19 | },
20 | });
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/apps/server/__tests__/packages/application/usecase/front/reservation/getRoomInfo.test.ts:
--------------------------------------------------------------------------------
1 | import { GetRoomInfoUseCase } from '$/packages/application/usecase/front/reservation/getRoomInfo';
2 | import { TestRoomRepository, getDummyRoomData } from '$/__tests__/__mocks__/infra/database/room';
3 | import { ResponseRepository } from '$/packages/infra/http/response';
4 |
5 | describe('GetRoomInfoUseCase', () => {
6 | it('should get room info', async() => {
7 | expect(await (new GetRoomInfoUseCase(new TestRoomRepository([getDummyRoomData()]), new ResponseRepository())).execute(1)).toEqual({
8 | status: 200,
9 | body: {
10 | id: 1,
11 | name: expect.any(String),
12 | number: expect.any(Number),
13 | price: expect.any(Number),
14 | createdAt: expect.any(Date),
15 | updatedAt: expect.any(Date),
16 | },
17 | });
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/apps/server/__tests__/packages/application/usecase/front/reservation/getSelectRooms.test.ts:
--------------------------------------------------------------------------------
1 | import { GetSelectRoomsUseCase } from '$/packages/application/usecase/front/reservation/getSelectRooms';
2 | import { TestRoomRepository, getDummyRoomData } from '$/__tests__/__mocks__/infra/database/room';
3 | import { ResponseRepository } from '$/packages/infra/http/response';
4 |
5 | describe('GetSelectRoomsUseCase', () => {
6 | it('should get select rooms', async() => {
7 | expect(await (new GetSelectRoomsUseCase(new TestRoomRepository([getDummyRoomData()]), new ResponseRepository())).execute()).toEqual({
8 | status: 200,
9 | body: [{
10 | id: 1,
11 | name: expect.any(String),
12 | number: expect.any(Number),
13 | price: expect.any(Number),
14 | createdAt: expect.any(Date),
15 | updatedAt: expect.any(Date),
16 | }],
17 | });
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/apps/server/__tests__/packages/application/usecase/front/reservation/validate.test.ts:
--------------------------------------------------------------------------------
1 | import { ValidateUseCase } from '$/packages/application/usecase/front/reservation/validate';
2 | import { ResponseRepository } from '$/packages/infra/http/response';
3 |
4 | describe('ValidateUseCase', () => {
5 | it('should return undefined', async() => {
6 | expect(await (new ValidateUseCase(new ResponseRepository())).execute()).toEqual({
7 | status: 200,
8 | body: undefined,
9 | });
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/apps/server/__tests__/packages/application/usecase/front/rooms/find.test.ts:
--------------------------------------------------------------------------------
1 | import { FindRoomUseCase } from '$/packages/application/usecase/front/rooms/find';
2 | import { TestRoomRepository, getDummyRoomData } from '$/__tests__/__mocks__/infra/database/room';
3 | import { ResponseRepository } from '$/packages/infra/http/response';
4 |
5 | describe('FindRoomUseCase', () => {
6 | it('should find room', async() => {
7 | expect(await (new FindRoomUseCase(new TestRoomRepository([getDummyRoomData()]), new ResponseRepository())).execute(1)).toEqual({
8 | status: 200,
9 | body: {
10 | id: 1,
11 | name: expect.any(String),
12 | number: expect.any(Number),
13 | price: expect.any(Number),
14 | createdAt: expect.any(Date),
15 | updatedAt: expect.any(Date),
16 | },
17 | });
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/apps/server/__tests__/packages/application/usecase/front/rooms/list.test.ts:
--------------------------------------------------------------------------------
1 | import { ListRoomsUseCase } from '$/packages/application/usecase/front/rooms/list';
2 | import { TestRoomRepository, getDummyRoomData } from '$/__tests__/__mocks__/infra/database/room';
3 | import { ResponseRepository } from '$/packages/infra/http/response';
4 |
5 | describe('ListRoomsUseCase', () => {
6 | it('should list rooms', async() => {
7 | expect(await (new ListRoomsUseCase(new TestRoomRepository([getDummyRoomData()]), new ResponseRepository())).execute()).toEqual({
8 | status: 200,
9 | body: [{
10 | id: 1,
11 | name: expect.any(String),
12 | number: expect.any(Number),
13 | price: expect.any(Number),
14 | createdAt: expect.any(Date),
15 | updatedAt: expect.any(Date),
16 | }],
17 | });
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/apps/server/__tests__/packages/application/usecase/lock/rooms/list.test.ts:
--------------------------------------------------------------------------------
1 | import { ListRoomsUseCase } from '$/packages/application/usecase/lock/rooms/list';
2 | import { TestRoomRepository, getDummyRoomData } from '$/__tests__/__mocks__/infra/database/room';
3 | import { ResponseRepository } from '$/packages/infra/http/response';
4 |
5 | describe('ListRoomsUseCase', () => {
6 | it('should get select rooms', async() => {
7 | expect(await (new ListRoomsUseCase(new TestRoomRepository([getDummyRoomData()]), new ResponseRepository())).execute()).toEqual({
8 | status: 200,
9 | body: [{
10 | id: 1,
11 | name: expect.any(String),
12 | number: expect.any(Number),
13 | price: expect.any(Number),
14 | createdAt: expect.any(Date),
15 | updatedAt: expect.any(Date),
16 | }],
17 | });
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/apps/server/__tests__/packages/application/usecase/stripe/detachPaymentMethod.test.ts:
--------------------------------------------------------------------------------
1 | import { DetachPaymentMethodUseCase } from '$/packages/application/usecase/stripe/detachPaymentMethod';
2 | import { TestPaymentRepository } from '$/__tests__/__mocks__/infra/payment';
3 | import { ResponseRepository } from '$/packages/infra/http/response';
4 |
5 | describe('DetachPaymentMethodUseCase', () => {
6 | it('detach payment method', async() => {
7 | expect(await (new DetachPaymentMethodUseCase(
8 | new TestPaymentRepository(),
9 | new ResponseRepository(),
10 | )).execute('pm_test')).toEqual({
11 | status: 200,
12 | body: {
13 | id: 'pm_test',
14 | card: {
15 | brand: 'visa',
16 | expMonth: 2,
17 | expYear: 22,
18 | last4: '2222',
19 | },
20 | },
21 | });
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/apps/server/__tests__/packages/application/usecase/stripe/getDefaultPaymentMethod.test.ts:
--------------------------------------------------------------------------------
1 | import { GetDefaultPaymentMethodUseCase } from '$/packages/application/usecase/stripe/getDefaultPaymentMethod';
2 | import { TestGuestRepository, getDummyGuestData } from '$/__tests__/__mocks__/infra/database/guest';
3 | import { TestPaymentRepository } from '$/__tests__/__mocks__/infra/payment';
4 | import { ResponseRepository } from '$/packages/infra/http/response';
5 |
6 | describe('GetDefaultPaymentMethodUseCase', () => {
7 | it('get default payment method', async() => {
8 | expect(await (new GetDefaultPaymentMethodUseCase(
9 | new TestGuestRepository([getDummyGuestData()]),
10 | new TestPaymentRepository(),
11 | new ResponseRepository(),
12 | )).execute(1)).toEqual({
13 | status: 200,
14 | body: { id: 'pm_test' },
15 | });
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/apps/server/__tests__/packages/application/usecase/stripe/handleWebhook.test.ts:
--------------------------------------------------------------------------------
1 | import { HandleWebhookUseCase } from '$/packages/application/usecase/stripe/handleWebhook';
2 | import { TestPaymentRepository } from '$/__tests__/__mocks__/infra/payment';
3 | import { ResponseRepository } from '$/packages/infra/http/response';
4 |
5 | describe('HandleWebhookUseCase', () => {
6 | it('attach payment method', async() => {
7 | expect(await (new HandleWebhookUseCase(
8 | new TestPaymentRepository(),
9 | new ResponseRepository(),
10 | )).execute({}, '')).toEqual({
11 | status: 200,
12 | body: { received: true },
13 | });
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/apps/server/__tests__/packages/infra/database/role.test.ts:
--------------------------------------------------------------------------------
1 | import { RoleRepository } from '$/packages/infra/database/role';
2 | import { getPromiseLikeItem } from '$/__tests__/utils';
3 |
4 | const repository = new RoleRepository();
5 |
6 | describe('list', () => {
7 | it('should return roles', async() => {
8 | const mock = jest.fn(() => getPromiseLikeItem([{ id: 1 }]));
9 | const injected = repository.list.inject({
10 | prisma: { role: { findMany: mock } },
11 | });
12 | expect(await injected()).toEqual([{ id: 1 }]);
13 | expect(mock).toBeCalledWith(undefined);
14 | });
15 | });
16 |
17 | describe('count', () => {
18 | it('should return number', async() => {
19 | const mock = jest.fn(() => getPromiseLikeItem(3));
20 | const injected = repository.count.inject({
21 | prisma: { role: { count: mock } },
22 | });
23 | expect(await injected()).toBe(3);
24 | expect(mock).toBeCalledWith(undefined);
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/apps/server/api/admin/admins/_adminId@number/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { FindAdminUseCase } from '$/packages/application/usecase/admin/admins/find';
4 | import { UpdateAdminUseCase } from '$/packages/application/usecase/admin/admins/update';
5 | import { DeleteAdminUseCase } from '$/packages/application/usecase/admin/admins/delete';
6 |
7 | export default defineController(() => ({
8 | get: async({ params }) => container.resolve(FindAdminUseCase).execute(params.adminId),
9 | patch: async({ params, body }) => container.resolve(UpdateAdminUseCase).execute(params.adminId, body),
10 | delete: async({ params }) => container.resolve(DeleteAdminUseCase).execute(params.adminId),
11 | }));
12 |
--------------------------------------------------------------------------------
/apps/server/api/admin/admins/_adminId@number/index.ts:
--------------------------------------------------------------------------------
1 | import type { AuthHeader } from '@frourio-demo/types';
2 | import type { Admin } from '$/packages/domain/database/admin';
3 | import type { AdminBody } from '$/validators';
4 |
5 | export type Methods = {
6 | get: {
7 | reqHeaders: AuthHeader;
8 | resBody: Admin;
9 | };
10 | patch: {
11 | reqHeaders: AuthHeader;
12 | reqFormat: FormData;
13 | reqBody: AdminBody;
14 | resBody: Admin;
15 | };
16 | delete: {
17 | reqHeaders: AuthHeader;
18 | resBody: Admin;
19 | };
20 | }
21 |
--------------------------------------------------------------------------------
/apps/server/api/admin/admins/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { ListAdminsUseCase } from '$/packages/application/usecase/admin/admins/list';
4 | import { CreateAdminUseCase } from '$/packages/application/usecase/admin/admins/create';
5 |
6 | export default defineController(() => ({
7 | get: async({ query }) => container.resolve(ListAdminsUseCase).execute(query),
8 | post: async({ body }) => container.resolve(CreateAdminUseCase).execute(body),
9 | }));
10 |
--------------------------------------------------------------------------------
/apps/server/api/admin/admins/hooks.ts:
--------------------------------------------------------------------------------
1 | import { defineHooks } from './$relay';
2 | import { verifyAdmin } from '$/packages/application/service/auth';
3 |
4 | export default defineHooks(() => ({
5 | onRequest: async(request, reply) => {
6 | if (!await verifyAdmin(request, 'admins')) {
7 | reply.status(401).send();
8 | }
9 | },
10 | }));
11 |
--------------------------------------------------------------------------------
/apps/server/api/admin/admins/index.ts:
--------------------------------------------------------------------------------
1 | import type { AuthHeader } from '@frourio-demo/types';
2 | import type { Admin } from '$/packages/domain/database/admin';
3 | import type { Query, QueryResult } from '@technote-space/material-table';
4 | import type { AdminBody } from '$/validators';
5 |
6 | export type Methods = {
7 | get: {
8 | reqHeaders: AuthHeader;
9 | query: Query;
10 | resBody: QueryResult;
11 | };
12 | post: {
13 | reqHeaders: AuthHeader;
14 | reqFormat: FormData;
15 | reqBody: AdminBody;
16 | resBody: Admin;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/apps/server/api/admin/admins/roles/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { ListRolesUseCase } from '$/packages/application/usecase/admin/admins/listRoles';
4 |
5 | export default defineController(() => ({
6 | get: async() => container.resolve(ListRolesUseCase).execute(),
7 | }));
8 |
--------------------------------------------------------------------------------
/apps/server/api/admin/admins/roles/index.ts:
--------------------------------------------------------------------------------
1 | import type { AuthHeader } from '@frourio-demo/types';
2 | import type { ListRole } from '$/packages/application/usecase/admin/admins/listRoles';
3 |
4 | export type Methods = {
5 | get: {
6 | reqHeaders: AuthHeader;
7 | resBody: ListRole;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/apps/server/api/admin/admins/search/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 |
3 | export default defineController(() => ({}));
4 |
--------------------------------------------------------------------------------
/apps/server/api/admin/admins/search/index.ts:
--------------------------------------------------------------------------------
1 | export type Methods = {}
2 |
--------------------------------------------------------------------------------
/apps/server/api/admin/admins/search/roles/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { SearchRoleUseCase } from '$/packages/application/usecase/admin/admins/searchRole';
4 |
5 | export default defineController(() => ({
6 | get: async({ query }) => container.resolve(SearchRoleUseCase).execute(query),
7 | }));
8 |
--------------------------------------------------------------------------------
/apps/server/api/admin/admins/search/roles/index.ts:
--------------------------------------------------------------------------------
1 | import type { AuthHeader } from '@frourio-demo/types';
2 | import type { Role } from '$/packages/domain/database/role';
3 | import type { Query, QueryResult } from '@technote-space/material-table';
4 |
5 | export type Methods = {
6 | get: {
7 | reqHeaders: AuthHeader;
8 | query: Query;
9 | resBody: QueryResult;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/apps/server/api/admin/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { GetAdminUseCase } from '$/packages/application/usecase/admin/getAdmin';
4 |
5 | export default defineController(() => ({
6 | get: async({ user }) => container.resolve(GetAdminUseCase).execute(user),
7 | }));
8 |
--------------------------------------------------------------------------------
/apps/server/api/admin/dashboard/cancel/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { CancelUseCase } from '$/packages/application/usecase/admin/dashboard/cancel';
4 |
5 | export default defineController(() => ({
6 | patch: async({ body }) => container.resolve(CancelUseCase).execute(body.id),
7 | }));
8 |
--------------------------------------------------------------------------------
/apps/server/api/admin/dashboard/cancel/index.ts:
--------------------------------------------------------------------------------
1 | import type { AuthHeader } from '@frourio-demo/types';
2 | import type { Reservation } from '$/packages/domain/database/reservation';
3 | import { CancelBody } from '$/validators';
4 |
5 | export type Methods = {
6 | patch: {
7 | reqHeaders: AuthHeader;
8 | reqBody: CancelBody;
9 | resBody: Reservation;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/apps/server/api/admin/dashboard/checkin/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { CheckinUseCase } from '$/packages/application/usecase/admin/dashboard/checkin';
4 | import { GetCheckinUseCase } from '$/packages/application/usecase/admin/dashboard/getCheckin';
5 | import { SendRoomKeyUseCase } from '$/packages/application/usecase/admin/dashboard/sendRoomKey';
6 |
7 | export default defineController(() => ({
8 | get: async({ query }) => container.resolve(GetCheckinUseCase).execute(query.query, query.date),
9 | patch: async({ body }) => container.resolve(CheckinUseCase).execute(body.id),
10 | post: async({ body }) => container.resolve(SendRoomKeyUseCase).execute(body.id),
11 | }));
12 |
--------------------------------------------------------------------------------
/apps/server/api/admin/dashboard/checkin/index.ts:
--------------------------------------------------------------------------------
1 | import type { AuthHeader } from '@frourio-demo/types';
2 | import type { CheckinReservation } from '$/packages/application/usecase/admin/dashboard/getCheckin';
3 | import type { Query, QueryResult } from '@technote-space/material-table';
4 | import type { Reservation } from '$/packages/domain/database/reservation';
5 | import type { CheckinBody, SendRoomKeyBody } from '$/validators';
6 |
7 | export type Methods = {
8 | get: {
9 | reqHeaders: AuthHeader;
10 | query: {
11 | query: Query;
12 | date?: Date;
13 | };
14 | resBody: QueryResult;
15 | };
16 | patch: {
17 | reqHeaders: AuthHeader;
18 | reqBody: CheckinBody;
19 | resBody: Reservation;
20 | };
21 | post: {
22 | reqHeaders: AuthHeader;
23 | reqBody: SendRoomKeyBody;
24 | resBody: Reservation;
25 | };
26 | }
27 |
--------------------------------------------------------------------------------
/apps/server/api/admin/dashboard/checkout/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { CheckoutUseCase } from '$/packages/application/usecase/admin/dashboard/checkout';
4 | import { GetCheckoutUseCase } from '$/packages/application/usecase/admin/dashboard/getCheckout';
5 |
6 | export default defineController(() => ({
7 | get: async({ query }) => container.resolve(GetCheckoutUseCase).execute(query.query, query.date),
8 | patch: async({ body }) => container.resolve(CheckoutUseCase).execute(body.id),
9 | }));
10 |
--------------------------------------------------------------------------------
/apps/server/api/admin/dashboard/checkout/index.ts:
--------------------------------------------------------------------------------
1 | import type { AuthHeader } from '@frourio-demo/types';
2 | import type { CheckoutReservation } from '$/packages/application/usecase/admin/dashboard/getCheckout';
3 | import type { Query, QueryResult } from '@technote-space/material-table';
4 | import type { Reservation } from '$/packages/domain/database/reservation';
5 | import { CheckoutBody } from '$/validators';
6 |
7 | export type Methods = {
8 | get: {
9 | reqHeaders: AuthHeader;
10 | query: {
11 | query: Query;
12 | date?: Date;
13 | };
14 | resBody: QueryResult;
15 | };
16 | patch: {
17 | reqHeaders: AuthHeader;
18 | reqBody: CheckoutBody;
19 | resBody: Reservation;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/apps/server/api/admin/dashboard/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 |
3 | export default defineController(() => ({}));
4 |
--------------------------------------------------------------------------------
/apps/server/api/admin/dashboard/hooks.ts:
--------------------------------------------------------------------------------
1 | import { defineHooks } from './$relay';
2 | import { verifyAdmin } from '$/packages/application/service/auth';
3 |
4 | export default defineHooks(() => ({
5 | onRequest: async(request, reply) => {
6 | if (!await verifyAdmin(request, 'dashboard')) {
7 | reply.status(401).send();
8 | }
9 | },
10 | }));
11 |
--------------------------------------------------------------------------------
/apps/server/api/admin/dashboard/index.ts:
--------------------------------------------------------------------------------
1 | export type Methods = {}
2 |
--------------------------------------------------------------------------------
/apps/server/api/admin/dashboard/rooms/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { GetSelectableRoomsUseCase } from '$/packages/application/usecase/admin/dashboard/getSelectableRooms';
4 |
5 | export default defineController(() => ({
6 | get: async() => container.resolve(GetSelectableRoomsUseCase).execute(),
7 | }));
8 |
--------------------------------------------------------------------------------
/apps/server/api/admin/dashboard/rooms/index.ts:
--------------------------------------------------------------------------------
1 | import type { AuthHeader } from '@frourio-demo/types';
2 | import type { SelectableRoom } from '$/packages/application/usecase/admin/dashboard/getSelectableRooms';
3 |
4 | export type Methods = {
5 | get: {
6 | reqHeaders: AuthHeader;
7 | resBody: SelectableRoom[];
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/apps/server/api/admin/dashboard/sales/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 |
3 | export default defineController(() => ({}));
4 |
--------------------------------------------------------------------------------
/apps/server/api/admin/dashboard/sales/daily/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { GetDailySalesUseCase } from '$/packages/application/usecase/admin/dashboard/getDailySales';
4 |
5 | export default defineController(() => ({
6 | get: async({ query }) => container.resolve(GetDailySalesUseCase).execute(query?.date ?? new Date(), query?.roomId),
7 | }));
8 |
--------------------------------------------------------------------------------
/apps/server/api/admin/dashboard/sales/daily/index.ts:
--------------------------------------------------------------------------------
1 | import type { AuthHeader } from '@frourio-demo/types';
2 | import type { DailySales } from '$/packages/application/usecase/admin/dashboard/types';
3 |
4 | export type Methods = {
5 | get: {
6 | reqHeaders: AuthHeader;
7 | query?: {
8 | date?: Date;
9 | roomId?: number;
10 | };
11 | resBody: DailySales[];
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/apps/server/api/admin/dashboard/sales/index.ts:
--------------------------------------------------------------------------------
1 | export type Methods = {}
2 |
--------------------------------------------------------------------------------
/apps/server/api/admin/dashboard/sales/monthly/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { GetMonthlySalesUseCase } from '$/packages/application/usecase/admin/dashboard/getMonthlySales';
4 |
5 | export default defineController(() => ({
6 | get: async({ query }) => container.resolve(GetMonthlySalesUseCase).execute(query?.date ?? new Date(), query?.roomId),
7 | }));
8 |
--------------------------------------------------------------------------------
/apps/server/api/admin/dashboard/sales/monthly/index.ts:
--------------------------------------------------------------------------------
1 | import type { AuthHeader } from '@frourio-demo/types';
2 | import type { MonthlySales } from '$/packages/application/usecase/admin/dashboard/types';
3 |
4 | export type Methods = {
5 | get: {
6 | reqHeaders: AuthHeader;
7 | query?: {
8 | date?: Date;
9 | roomId?: number;
10 | };
11 | resBody: MonthlySales[];
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/apps/server/api/admin/guests/_guestId@number/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { FindGuestUseCase } from '$/packages/application/usecase/admin/guests/find';
4 | import { UpdateGuestUseCase } from '$/packages/application/usecase/admin/guests/update';
5 | import { DeleteGuestUseCase } from '$/packages/application/usecase/admin/guests/delete';
6 |
7 | export default defineController(() => ({
8 | get: async({ params }) => container.resolve(FindGuestUseCase).execute(params.guestId),
9 | patch: async({ params, body }) => container.resolve(UpdateGuestUseCase).execute(params.guestId, body),
10 | delete: async({ params }) => container.resolve(DeleteGuestUseCase).execute(params.guestId),
11 | }));
12 |
--------------------------------------------------------------------------------
/apps/server/api/admin/guests/_guestId@number/index.ts:
--------------------------------------------------------------------------------
1 | import type { AuthHeader } from '@frourio-demo/types';
2 | import type { Guest } from '$/packages/domain/database/guest';
3 | import type { GuestBody } from '$/validators';
4 |
5 | export type Methods = {
6 | get: {
7 | reqHeaders: AuthHeader;
8 | resBody: Guest;
9 | };
10 | patch: {
11 | reqHeaders: AuthHeader;
12 | reqBody: GuestBody;
13 | resBody: Guest;
14 | };
15 | delete: {
16 | reqHeaders: AuthHeader;
17 | resBody: Guest;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/apps/server/api/admin/guests/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { ListGuestsUseCase } from '$/packages/application/usecase/admin/guests/list';
4 | import { CreateGuestUseCase } from '$/packages/application/usecase/admin/guests/create';
5 |
6 | export default defineController(() => ({
7 | get: async({ query }) => container.resolve(ListGuestsUseCase).execute(query),
8 | post: async({ body }) => container.resolve(CreateGuestUseCase).execute(body),
9 | }));
10 |
--------------------------------------------------------------------------------
/apps/server/api/admin/guests/hooks.ts:
--------------------------------------------------------------------------------
1 | import { defineHooks } from './$relay';
2 | import { verifyAdmin } from '$/packages/application/service/auth';
3 |
4 | export default defineHooks(() => ({
5 | onRequest: async(request, reply) => {
6 | if (!await verifyAdmin(request, 'guests')) {
7 | reply.status(401).send();
8 | }
9 | },
10 | }));
11 |
--------------------------------------------------------------------------------
/apps/server/api/admin/guests/index.ts:
--------------------------------------------------------------------------------
1 | import type { AuthHeader } from '@frourio-demo/types';
2 | import type { Guest } from '$/packages/domain/database/guest';
3 | import type { Query, QueryResult } from '@technote-space/material-table';
4 | import type { GuestBody } from '$/validators';
5 |
6 | export type Methods = {
7 | get: {
8 | reqHeaders: AuthHeader;
9 | query: Query;
10 | resBody: QueryResult;
11 | };
12 | post: {
13 | reqHeaders: AuthHeader;
14 | reqBody: GuestBody;
15 | resBody: Guest;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/apps/server/api/admin/index.ts:
--------------------------------------------------------------------------------
1 | import type { AuthHeader } from '@frourio-demo/types';
2 | import type { Admin } from '$/packages/domain/database/admin';
3 |
4 | export type Methods = {
5 | get: {
6 | reqHeaders: AuthHeader;
7 | resBody: Admin;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/apps/server/api/admin/login/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { LoginUseCase } from '$/packages/application/usecase/admin/login/login';
4 |
5 | export default defineController((fastify) => ({
6 | post: ({ body }) => container.resolve(LoginUseCase).execute(body, fastify),
7 | }));
8 |
--------------------------------------------------------------------------------
/apps/server/api/admin/login/index.ts:
--------------------------------------------------------------------------------
1 | import type { AuthHeader } from '@frourio-demo/types';
2 | import type { LoginBody } from '$/validators';
3 |
4 | export type Methods = {
5 | post: {
6 | reqBody: LoginBody;
7 | resHeaders?: AuthHeader;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/apps/server/api/admin/reservations/_reservationId@number/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { FindReservationUseCase } from '$/packages/application/usecase/admin/reservations/find';
4 | import { UpdateReservationUseCase } from '$/packages/application/usecase/admin/reservations/update';
5 | import { DeleteReservationUseCase } from '$/packages/application/usecase/admin/reservations/delete';
6 |
7 | export default defineController(() => ({
8 | get: async({ params }) => container.resolve(FindReservationUseCase).execute(params.reservationId),
9 | patch: async({ params, body }) => container.resolve(UpdateReservationUseCase).execute(params.reservationId, body),
10 | delete: async({ params }) => container.resolve(DeleteReservationUseCase).execute(params.reservationId),
11 | }));
12 |
--------------------------------------------------------------------------------
/apps/server/api/admin/reservations/_reservationId@number/index.ts:
--------------------------------------------------------------------------------
1 | import type { AuthHeader } from '@frourio-demo/types';
2 | import type { Reservation } from '$/packages/domain/database/reservation';
3 | import type { ReservationBody } from '$/validators';
4 |
5 | export type Methods = {
6 | get: {
7 | reqHeaders: AuthHeader;
8 | resBody: Reservation;
9 | };
10 | patch: {
11 | reqHeaders: AuthHeader;
12 | reqBody: ReservationBody;
13 | resBody: Reservation;
14 | };
15 | delete: {
16 | reqHeaders: AuthHeader;
17 | resBody: Reservation;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/apps/server/api/admin/reservations/calendar/checkin/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { GetCheckinNotSelectableUseCase } from '$/packages/application/usecase/admin/reservations/getCheckinNotSelectable';
4 |
5 | export default defineController(() => ({
6 | get: async({
7 | query: { roomId, start, end, id },
8 | }) => container.resolve(GetCheckinNotSelectableUseCase).execute(roomId, start, end, id),
9 | }));
10 |
--------------------------------------------------------------------------------
/apps/server/api/admin/reservations/calendar/checkin/index.ts:
--------------------------------------------------------------------------------
1 | import type { AuthHeader } from '@frourio-demo/types';
2 | import type { CheckinNotSelectableEvent } from '$/packages/application/usecase/admin/reservations/getCheckinNotSelectable';
3 |
4 | export type Methods = {
5 | get: {
6 | reqHeaders: AuthHeader;
7 | query: {
8 | roomId: number;
9 | start: Date;
10 | end: Date;
11 | id?: number;
12 | };
13 | resBody: CheckinNotSelectableEvent[];
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/apps/server/api/admin/reservations/calendar/checkout/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { GetCheckoutSelectableUseCase } from '$/packages/application/usecase/admin/reservations/getCheckoutSelectable';
4 |
5 | export default defineController(() => ({
6 | get: async({
7 | query: { roomId, end, checkin, id },
8 | }) => container.resolve(GetCheckoutSelectableUseCase).execute(roomId, end, checkin, id),
9 | }));
10 |
--------------------------------------------------------------------------------
/apps/server/api/admin/reservations/calendar/checkout/index.ts:
--------------------------------------------------------------------------------
1 | import type { AuthHeader } from '@frourio-demo/types';
2 | import type { CheckoutSelectableEvent } from '$/packages/application/usecase/admin/reservations/getCheckoutSelectable';
3 |
4 | export type Methods = {
5 | get: {
6 | reqHeaders: AuthHeader;
7 | query: {
8 | roomId: number;
9 | end: Date;
10 | checkin: Date;
11 | id?: number;
12 | };
13 | resBody: CheckoutSelectableEvent[];
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/apps/server/api/admin/reservations/calendar/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 |
3 | export default defineController(() => ({}));
4 |
--------------------------------------------------------------------------------
/apps/server/api/admin/reservations/calendar/index.ts:
--------------------------------------------------------------------------------
1 | export type Methods = {}
2 |
--------------------------------------------------------------------------------
/apps/server/api/admin/reservations/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { ListReservationsUseCase } from '$/packages/application/usecase/admin/reservations/list';
4 | import { CreateReservationUseCase } from '$/packages/application/usecase/admin/reservations/create';
5 |
6 | export default defineController(() => ({
7 | get: async({ query }) => container.resolve(ListReservationsUseCase).execute(query),
8 | post: async({ body }) => container.resolve(CreateReservationUseCase).execute(body),
9 | }));
10 |
--------------------------------------------------------------------------------
/apps/server/api/admin/reservations/guest/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { GetSelectedGuestUseCase } from '$/packages/application/usecase/admin/reservations/getSelectedGuest';
4 |
5 | export default defineController(() => ({
6 | get: async({ query: { guestId } }) => container.resolve(GetSelectedGuestUseCase).execute(guestId),
7 | }));
8 |
--------------------------------------------------------------------------------
/apps/server/api/admin/reservations/guest/index.ts:
--------------------------------------------------------------------------------
1 | import type { AuthHeader } from '@frourio-demo/types';
2 | import type { SelectedGuest } from '$/packages/application/usecase/admin/reservations/getSelectedGuest';
3 |
4 | export type Methods = {
5 | get: {
6 | reqHeaders: AuthHeader;
7 | query: {
8 | guestId: number;
9 | };
10 | resBody: SelectedGuest
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/apps/server/api/admin/reservations/hooks.ts:
--------------------------------------------------------------------------------
1 | import { defineHooks } from './$relay';
2 | import { verifyAdmin } from '$/packages/application/service/auth';
3 |
4 | export default defineHooks(() => ({
5 | onRequest: async(request, reply) => {
6 | if (!await verifyAdmin(request, 'reservations')) {
7 | reply.status(401).send();
8 | }
9 | },
10 | }));
11 |
--------------------------------------------------------------------------------
/apps/server/api/admin/reservations/index.ts:
--------------------------------------------------------------------------------
1 | import type { AuthHeader } from '@frourio-demo/types';
2 | import type { Reservation } from '$/packages/domain/database/reservation';
3 | import type { Query, QueryResult } from '@technote-space/material-table';
4 | import type { ListReservation } from '$/packages/application/usecase/admin/reservations/list';
5 | import type { ReservationBody } from '$/validators';
6 |
7 | export type Methods = {
8 | get: {
9 | reqHeaders: AuthHeader;
10 | query: Query;
11 | resBody: QueryResult;
12 | };
13 | post: {
14 | reqHeaders: AuthHeader;
15 | reqBody: ReservationBody;
16 | resBody: Reservation;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/apps/server/api/admin/reservations/room/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { GetSelectedRoomUseCase } from '$/packages/application/usecase/admin/reservations/getSelectedRoom';
4 |
5 | export default defineController(() => ({
6 | get: async({ query: { roomId } }) => container.resolve(GetSelectedRoomUseCase).execute(roomId),
7 | }));
8 |
--------------------------------------------------------------------------------
/apps/server/api/admin/reservations/room/index.ts:
--------------------------------------------------------------------------------
1 | import type { AuthHeader } from '@frourio-demo/types';
2 | import type { SelectedRoom } from '$/packages/application/usecase/admin/reservations/getSelectedRoom';
3 |
4 | export type Methods = {
5 | get: {
6 | reqHeaders: AuthHeader;
7 | query: {
8 | roomId: number;
9 | };
10 | resBody: SelectedRoom;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/apps/server/api/admin/reservations/search/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 |
3 | export default defineController(() => ({}));
4 |
--------------------------------------------------------------------------------
/apps/server/api/admin/reservations/search/guests/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { SearchGuestUseCase } from '$/packages/application/usecase/admin/reservations/searchGuest';
4 |
5 | export default defineController(() => ({
6 | get: async({ query }) => container.resolve(SearchGuestUseCase).execute(query),
7 | }));
8 |
--------------------------------------------------------------------------------
/apps/server/api/admin/reservations/search/guests/index.ts:
--------------------------------------------------------------------------------
1 | import type { AuthHeader } from '@frourio-demo/types';
2 | import type { Guest } from '$/packages/domain/database/guest';
3 | import type { Query, QueryResult } from '@technote-space/material-table';
4 |
5 | export type Methods = {
6 | get: {
7 | reqHeaders: AuthHeader;
8 | query: Query;
9 | resBody: QueryResult;
10 | };
11 | }
12 |
--------------------------------------------------------------------------------
/apps/server/api/admin/reservations/search/index.ts:
--------------------------------------------------------------------------------
1 | export type Methods = {}
2 |
--------------------------------------------------------------------------------
/apps/server/api/admin/reservations/search/rooms/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { SearchRoomUseCase } from '$/packages/application/usecase/admin/reservations/searchRoom';
4 |
5 | export default defineController(() => ({
6 | get: async({ query }) => container.resolve(SearchRoomUseCase).execute(query),
7 | }));
8 |
--------------------------------------------------------------------------------
/apps/server/api/admin/reservations/search/rooms/index.ts:
--------------------------------------------------------------------------------
1 | import type { AuthHeader } from '@frourio-demo/types';
2 | import type { Room } from '$/packages/domain/database/room';
3 | import type { Query, QueryResult } from '@technote-space/material-table';
4 |
5 | export type Methods = {
6 | get: {
7 | reqHeaders: AuthHeader;
8 | query: Query;
9 | resBody: QueryResult;
10 | };
11 | }
12 |
--------------------------------------------------------------------------------
/apps/server/api/admin/rooms/_roomId@number/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { FindRoomUseCase } from '$/packages/application/usecase/admin/rooms/find';
4 | import { UpdateRoomUseCase } from '$/packages/application/usecase/admin/rooms/update';
5 | import { DeleteRoomUseCase } from '$/packages/application/usecase/admin/rooms/delete';
6 |
7 | export default defineController(() => ({
8 | get: async({ params }) => container.resolve(FindRoomUseCase).execute(params.roomId),
9 | patch: async({ params, body }) => container.resolve(UpdateRoomUseCase).execute(params.roomId, body),
10 | delete: async({ params }) => container.resolve(DeleteRoomUseCase).execute(params.roomId),
11 | }));
12 |
--------------------------------------------------------------------------------
/apps/server/api/admin/rooms/_roomId@number/index.ts:
--------------------------------------------------------------------------------
1 | import type { AuthHeader } from '@frourio-demo/types';
2 | import type { Room } from '$/packages/domain/database/room';
3 | import type { RoomBody } from '$/validators';
4 |
5 | export type Methods = {
6 | get: {
7 | reqHeaders: AuthHeader;
8 | resBody: Room;
9 | };
10 | patch: {
11 | reqHeaders: AuthHeader;
12 | reqBody: RoomBody;
13 | resBody: Room;
14 | };
15 | delete: {
16 | reqHeaders: AuthHeader;
17 | resBody: Room;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/apps/server/api/admin/rooms/calendar/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { GetStatusCalendarEventsUseCase } from '$/packages/application/usecase/admin/rooms/getStatusCalendarEvents';
4 |
5 | export default defineController(() => ({
6 | get: async({
7 | query: { roomId, start, end },
8 | }) => container.resolve(GetStatusCalendarEventsUseCase).execute(roomId, start, end),
9 | }));
10 |
--------------------------------------------------------------------------------
/apps/server/api/admin/rooms/calendar/index.ts:
--------------------------------------------------------------------------------
1 | import type { AuthHeader } from '@frourio-demo/types';
2 | import type { RoomStatusEvent } from '$/packages/application/usecase/admin/rooms/getStatusCalendarEvents';
3 |
4 | export type Methods = {
5 | get: {
6 | reqHeaders: AuthHeader;
7 | query: {
8 | roomId: number;
9 | start: Date;
10 | end: Date;
11 | };
12 | resBody: RoomStatusEvent[];
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/apps/server/api/admin/rooms/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { ListRoomsUseCase } from '$/packages/application/usecase/admin/rooms/list';
4 | import { CreateRoomUseCase } from '$/packages/application/usecase/admin/rooms/create';
5 |
6 | export default defineController(() => ({
7 | get: async({ query }) => container.resolve(ListRoomsUseCase).execute(query),
8 | post: async({ body }) => container.resolve(CreateRoomUseCase).execute(body),
9 | }));
10 |
--------------------------------------------------------------------------------
/apps/server/api/admin/rooms/hooks.ts:
--------------------------------------------------------------------------------
1 | import { defineHooks } from './$relay';
2 | import { verifyAdmin } from '$/packages/application/service/auth';
3 |
4 | export default defineHooks(() => ({
5 | onRequest: async(request, reply) => {
6 | if (!await verifyAdmin(request, 'rooms')) {
7 | reply.status(401).send();
8 | }
9 | },
10 | }));
11 |
--------------------------------------------------------------------------------
/apps/server/api/admin/rooms/index.ts:
--------------------------------------------------------------------------------
1 | import type { AuthHeader } from '@frourio-demo/types';
2 | import type { Room } from '$/packages/domain/database/room';
3 | import type { Query, QueryResult } from '@technote-space/material-table';
4 | import type { RoomBody } from '$/validators';
5 |
6 | export type Methods = {
7 | get: {
8 | reqHeaders: AuthHeader;
9 | query: Query;
10 | resBody: QueryResult;
11 | };
12 | post: {
13 | reqHeaders: AuthHeader;
14 | reqBody: RoomBody;
15 | resBody: Room;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/apps/server/api/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 |
3 | export default defineController(() => ({}));
4 |
--------------------------------------------------------------------------------
/apps/server/api/front/account/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 |
3 | export default defineController(() => ({}));
4 |
--------------------------------------------------------------------------------
/apps/server/api/front/account/guest/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { GetGuestInfoUseCase } from '$/packages/application/usecase/front/account/getGuestInfo';
4 | import { UpdateGuestInfoUseCase } from '$/packages/application/usecase/front/account/updateGuestInfo';
5 |
6 | export default defineController(() => ({
7 | get: async({ user }) => container.resolve(GetGuestInfoUseCase).execute(user),
8 | patch: async({ user, body }) => container.resolve(UpdateGuestInfoUseCase).execute(user, body),
9 | }));
10 |
--------------------------------------------------------------------------------
/apps/server/api/front/account/guest/index.ts:
--------------------------------------------------------------------------------
1 | import type { AuthHeader } from '@frourio-demo/types';
2 | import type { Guest } from '$/packages/domain/database/guest';
3 | import type { AccountBody } from '$/validators';
4 |
5 | export type Methods = {
6 | get: {
7 | reqHeaders: AuthHeader;
8 | resBody: Guest;
9 | };
10 | patch: {
11 | reqHeaders: AuthHeader;
12 | reqBody: AccountBody;
13 | resBody: Guest;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/apps/server/api/front/account/hooks.ts:
--------------------------------------------------------------------------------
1 | import type { GuestAuthorizationPayload } from '$/packages/application/service/auth';
2 | import { defineHooks } from './$relay';
3 | import { verifyGuest } from '$/packages/application/service/auth';
4 |
5 | export type AdditionalRequest = {
6 | user: GuestAuthorizationPayload;
7 | }
8 |
9 | export default defineHooks(() => ({
10 | onRequest: async(request, reply) => {
11 | if (!await verifyGuest(request)) {
12 | reply.status(401).send({ tokenExpired: true });
13 | }
14 | },
15 | }));
16 |
--------------------------------------------------------------------------------
/apps/server/api/front/account/index.ts:
--------------------------------------------------------------------------------
1 | export type Methods = {}
2 |
--------------------------------------------------------------------------------
/apps/server/api/front/account/reservations/cancelled/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { GetCancelledReservationsUseCase } from '$/packages/application/usecase/front/account/getCancelledReservations';
4 |
5 | export default defineController(() => ({
6 | get: async({ user }) => container.resolve(GetCancelledReservationsUseCase).execute(user),
7 | }));
8 |
--------------------------------------------------------------------------------
/apps/server/api/front/account/reservations/cancelled/index.ts:
--------------------------------------------------------------------------------
1 | import type { AuthHeader } from '@frourio-demo/types';
2 | import type { Reservation } from '$/packages/domain/database/reservation';
3 |
4 | export type Methods = {
5 | get: {
6 | reqHeaders: AuthHeader;
7 | resBody: Reservation[]
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/apps/server/api/front/account/reservations/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 |
3 | export default defineController(() => ({}));
4 |
--------------------------------------------------------------------------------
/apps/server/api/front/account/reservations/index.ts:
--------------------------------------------------------------------------------
1 | export type Methods = {}
2 |
--------------------------------------------------------------------------------
/apps/server/api/front/account/reservations/paid/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { GetPaidReservationsUseCase } from '$/packages/application/usecase/front/account/getPaidReservations';
4 |
5 | export default defineController(() => ({
6 | get: async({ user }) => container.resolve(GetPaidReservationsUseCase).execute(user),
7 | }));
8 |
--------------------------------------------------------------------------------
/apps/server/api/front/account/reservations/paid/index.ts:
--------------------------------------------------------------------------------
1 | import type { AuthHeader } from '@frourio-demo/types';
2 | import type { Reservation } from '$/packages/domain/database/reservation';
3 |
4 | export type Methods = {
5 | get: {
6 | reqHeaders: AuthHeader;
7 | resBody: Reservation[]
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/apps/server/api/front/account/reservations/reserved/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { GetReservedReservationsUseCase } from '$/packages/application/usecase/front/account/getReservedReservations';
4 |
5 | export default defineController(() => ({
6 | get: async({ user }) => container.resolve(GetReservedReservationsUseCase).execute(user),
7 | }));
8 |
--------------------------------------------------------------------------------
/apps/server/api/front/account/reservations/reserved/index.ts:
--------------------------------------------------------------------------------
1 | import type { AuthHeader } from '@frourio-demo/types';
2 | import type { Reservation } from '$/packages/domain/database/reservation';
3 |
4 | export type Methods = {
5 | get: {
6 | reqHeaders: AuthHeader;
7 | resBody: Reservation[]
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/apps/server/api/front/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 |
3 | export default defineController(() => ({}));
4 |
--------------------------------------------------------------------------------
/apps/server/api/front/guest/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { FindGuestUseCase } from '$/packages/application/usecase/front/guest/find';
4 |
5 | export default defineController(() => ({
6 | get: async({ user }) => container.resolve(FindGuestUseCase).execute(user),
7 | }));
8 |
--------------------------------------------------------------------------------
/apps/server/api/front/guest/hooks.ts:
--------------------------------------------------------------------------------
1 | import type { GuestAuthorizationPayload } from '$/packages/application/service/auth';
2 | import { defineHooks } from './$relay';
3 | import { verifyGuest } from '$/packages/application/service/auth';
4 |
5 | export type AdditionalRequest = {
6 | user: GuestAuthorizationPayload;
7 | }
8 |
9 | export default defineHooks(() => ({
10 | onRequest: async(request, reply) => {
11 | if (!await verifyGuest(request)) {
12 | reply.status(401).send({ tokenExpired: true });
13 | }
14 | },
15 | }));
16 |
--------------------------------------------------------------------------------
/apps/server/api/front/guest/index.ts:
--------------------------------------------------------------------------------
1 | import type { AuthHeader } from '@frourio-demo/types';
2 | import type { Guest } from '$/packages/domain/database/guest';
3 |
4 | export type Methods = {
5 | get: {
6 | reqHeaders: AuthHeader;
7 | resBody: Guest;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/apps/server/api/front/hooks.ts:
--------------------------------------------------------------------------------
1 | import { defineHooks } from './$relay';
2 | import { parseQuery } from '$/packages/application/service/table';
3 | import { processMultipartFormDataBody } from '$/packages/application/service/multipart';
4 |
5 | export default defineHooks(() => ({
6 | preHandler: (request, reply, done) => {
7 | if (request.query && typeof request.query === 'object') {
8 | if ('query' in request.query) {
9 | request.query['query'] = parseQuery(request.query['query']);
10 | }
11 | request.query = parseQuery(request.query);
12 | }
13 | if (typeof request.body === 'object' && request.headers['content-type'] && /^multipart\/form-data/.test(request.headers['content-type'])) {
14 | request.body = processMultipartFormDataBody(request.body as {});
15 | }
16 |
17 | done();
18 | },
19 | }));
20 |
--------------------------------------------------------------------------------
/apps/server/api/front/index.ts:
--------------------------------------------------------------------------------
1 | export type Methods = {}
2 |
--------------------------------------------------------------------------------
/apps/server/api/front/login/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { LoginUseCase } from '$/packages/application/usecase/front/login/login';
4 |
5 | export default defineController((fastify) => ({
6 | post: ({ body }) => container.resolve(LoginUseCase).execute(body, fastify),
7 | }));
8 |
--------------------------------------------------------------------------------
/apps/server/api/front/login/index.ts:
--------------------------------------------------------------------------------
1 | import type { AuthHeader } from '@frourio-demo/types';
2 | import type { Auth0Body } from '$/validators';
3 |
4 | export type Methods = {
5 | post: {
6 | reqBody: Auth0Body;
7 | resHeaders?: AuthHeader;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/apps/server/api/front/reservation/calendar/checkin/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { GetCheckinNotSelectableUseCase } from '$/packages/application/usecase/front/reservation/getCheckinNotSelectable';
4 |
5 | export default defineController(() => ({
6 | get: async(
7 | {
8 | query: { roomId, start, end },
9 | user,
10 | },
11 | ) => container.resolve(GetCheckinNotSelectableUseCase).execute(roomId, start, end, user),
12 | }));
13 |
--------------------------------------------------------------------------------
/apps/server/api/front/reservation/calendar/checkin/index.ts:
--------------------------------------------------------------------------------
1 | import type { AuthHeader } from '@frourio-demo/types';
2 | import type { CheckinNotSelectableEvent } from '$/packages/application/usecase/front/reservation/getCheckinNotSelectable';
3 |
4 | export type Methods = {
5 | get: {
6 | reqHeaders?: AuthHeader;
7 | query: {
8 | roomId: number;
9 | start: Date;
10 | end: Date;
11 | };
12 | resBody: CheckinNotSelectableEvent[];
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/apps/server/api/front/reservation/calendar/checkout/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { GetCheckoutSelectableUseCase } from '$/packages/application/usecase/front/reservation/getCheckoutSelectable';
4 |
5 | export default defineController(() => ({
6 | get: async(
7 | {
8 | query: { roomId, end, checkin },
9 | user,
10 | },
11 | ) => container.resolve(GetCheckoutSelectableUseCase).execute(roomId, end, checkin, user),
12 | }));
13 |
--------------------------------------------------------------------------------
/apps/server/api/front/reservation/calendar/checkout/index.ts:
--------------------------------------------------------------------------------
1 | import type { AuthHeader } from '@frourio-demo/types';
2 | import type { CheckoutSelectableEvent } from '$/packages/application/usecase/front/reservation/getCheckoutSelectable';
3 |
4 | export type Methods = {
5 | get: {
6 | reqHeaders?: AuthHeader;
7 | query: {
8 | roomId: number;
9 | end: Date;
10 | checkin: Date;
11 | };
12 | resBody: CheckoutSelectableEvent[];
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/apps/server/api/front/reservation/calendar/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 |
3 | export default defineController(() => ({}));
4 |
--------------------------------------------------------------------------------
/apps/server/api/front/reservation/calendar/index.ts:
--------------------------------------------------------------------------------
1 | export type Methods = {}
2 |
--------------------------------------------------------------------------------
/apps/server/api/front/reservation/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { ReserveUseCase } from '$/packages/application/usecase/front/reservation/reserve';
4 |
5 | export default defineController(() => ({
6 | post: async({ body, user }) => container.resolve(ReserveUseCase).execute(body, user),
7 | }));
8 |
--------------------------------------------------------------------------------
/apps/server/api/front/reservation/guest/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { GetGuestInfoUseCase } from '$/packages/application/usecase/front/reservation/getGuestInfo';
4 |
5 | export default defineController(() => ({
6 | get: async({ user }) => container.resolve(GetGuestInfoUseCase).execute(user),
7 | }));
8 |
--------------------------------------------------------------------------------
/apps/server/api/front/reservation/guest/index.ts:
--------------------------------------------------------------------------------
1 | import type { AuthHeader } from '@frourio-demo/types';
2 | import type { Guest } from '$/packages/domain/database/guest';
3 |
4 | export type Methods = {
5 | get: {
6 | reqHeaders?: AuthHeader;
7 | resBody: Guest | undefined;
8 | };
9 | }
10 |
--------------------------------------------------------------------------------
/apps/server/api/front/reservation/hooks.ts:
--------------------------------------------------------------------------------
1 | import type { GuestAuthorizationPayload } from '$/packages/application/service/auth';
2 | import { defineHooks } from './$relay';
3 | import { verifyGuest } from '$/packages/application/service/auth';
4 |
5 | export type AdditionalRequest = {
6 | user?: GuestAuthorizationPayload;
7 | }
8 |
9 | export default defineHooks(() => ({
10 | onRequest: async(request) => {
11 | await verifyGuest(request);
12 | },
13 | }));
14 |
--------------------------------------------------------------------------------
/apps/server/api/front/reservation/index.ts:
--------------------------------------------------------------------------------
1 | import type { AuthHeader } from '@frourio-demo/types';
2 | import type { Reservation } from '$/packages/domain/database/reservation';
3 | import type { CreateReservationBody } from '$/validators';
4 |
5 | export type Methods = {
6 | post: {
7 | reqHeaders?: AuthHeader;
8 | reqBody: CreateReservationBody;
9 | resBody: Reservation;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/apps/server/api/front/reservation/rooms/_roomId@number/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { GetRoomInfoUseCase } from '$/packages/application/usecase/front/reservation/getRoomInfo';
4 |
5 | export default defineController(() => ({
6 | get: async({ params }) => container.resolve(GetRoomInfoUseCase).execute(params.roomId),
7 | }));
8 |
--------------------------------------------------------------------------------
/apps/server/api/front/reservation/rooms/_roomId@number/index.ts:
--------------------------------------------------------------------------------
1 | import type { Room } from '$/packages/domain/database/room';
2 |
3 | export type Methods = {
4 | get: {
5 | resBody: Room
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/apps/server/api/front/reservation/rooms/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { GetSelectRoomsUseCase } from '$/packages/application/usecase/front/reservation/getSelectRooms';
4 |
5 | export default defineController(() => ({
6 | get: async() => container.resolve(GetSelectRoomsUseCase).execute(),
7 | }));
8 |
--------------------------------------------------------------------------------
/apps/server/api/front/reservation/rooms/index.ts:
--------------------------------------------------------------------------------
1 | import type { Room } from '$/packages/domain/database/room';
2 |
3 | export type Methods = {
4 | get: {
5 | resBody: Room[];
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/apps/server/api/front/reservation/validate/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { ValidateUseCase } from '$/packages/application/usecase/front/reservation/validate';
4 |
5 | export default defineController(() => ({
6 | post: () => container.resolve(ValidateUseCase).execute(),
7 | }));
8 |
--------------------------------------------------------------------------------
/apps/server/api/front/reservation/validate/index.ts:
--------------------------------------------------------------------------------
1 | import type { AuthHeader } from '@frourio-demo/types';
2 | import type { ValidateReservationBody } from '$/validators';
3 |
4 | export type Methods = {
5 | post: {
6 | reqHeaders?: AuthHeader;
7 | reqBody: ValidateReservationBody;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/apps/server/api/front/reservations/_code@string/cancel/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { CancelUseCase } from '$/packages/application/usecase/front/reservations/cancel';
4 |
5 | export default defineController(() => ({
6 | patch: async({ params }) => container.resolve(CancelUseCase).execute(params.code),
7 | }));
8 |
--------------------------------------------------------------------------------
/apps/server/api/front/reservations/_code@string/cancel/index.ts:
--------------------------------------------------------------------------------
1 | import type { Reservation } from '$/packages/domain/database/reservation';
2 |
3 | export type Methods = {
4 | patch: {
5 | resBody: Reservation;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/apps/server/api/front/reservations/_code@string/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { GetReservationDetailUseCase } from '$/packages/application/usecase/front/reservations/getReservationDetail';
4 |
5 | export default defineController(() => ({
6 | get: async({ params }) => container.resolve(GetReservationDetailUseCase).execute(params.code),
7 | }));
8 |
--------------------------------------------------------------------------------
/apps/server/api/front/reservations/_code@string/index.ts:
--------------------------------------------------------------------------------
1 | import type { ReservationDetail } from '$/packages/application/usecase/front/reservations/getReservationDetail';
2 |
3 | export type Methods = {
4 | get: {
5 | resBody: ReservationDetail;
6 | };
7 | }
8 |
--------------------------------------------------------------------------------
/apps/server/api/front/reservations/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 |
3 | export default defineController(() => ({}));
4 |
--------------------------------------------------------------------------------
/apps/server/api/front/reservations/index.ts:
--------------------------------------------------------------------------------
1 | export type Methods = {}
2 |
--------------------------------------------------------------------------------
/apps/server/api/front/rooms/_roomId@number/calendar/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { CalendarUseCase } from '$/packages/application/usecase/front/rooms/calendar';
4 |
5 | export default defineController(() => ({
6 | get: async({
7 | params,
8 | query: { start, end },
9 | }) => container.resolve(CalendarUseCase).execute(params.roomId, start, end),
10 | }));
11 |
--------------------------------------------------------------------------------
/apps/server/api/front/rooms/_roomId@number/calendar/index.ts:
--------------------------------------------------------------------------------
1 | import type { NotSelectableEvent } from '$/packages/application/usecase/front/rooms/calendar';
2 |
3 | export type Methods = {
4 | get: {
5 | query: {
6 | start: Date;
7 | end: Date;
8 | };
9 | resBody: NotSelectableEvent[];
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/apps/server/api/front/rooms/_roomId@number/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { FindRoomUseCase } from '$/packages/application/usecase/front/rooms/find';
4 |
5 | export default defineController(() => ({
6 | get: async({ params }) => container.resolve(FindRoomUseCase).execute(params.roomId),
7 | }));
8 |
--------------------------------------------------------------------------------
/apps/server/api/front/rooms/_roomId@number/index.ts:
--------------------------------------------------------------------------------
1 | import type { Room } from '$/packages/domain/database/room';
2 |
3 | export type Methods = {
4 | get: {
5 | resBody: Room;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/apps/server/api/front/rooms/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { ListRoomsUseCase } from '$/packages/application/usecase/front/rooms/list';
4 |
5 | export default defineController(() => ({
6 | get: async() => container.resolve(ListRoomsUseCase).execute(),
7 | }));
8 |
--------------------------------------------------------------------------------
/apps/server/api/front/rooms/index.ts:
--------------------------------------------------------------------------------
1 | import type { Room } from '$/packages/domain/database/room';
2 |
3 | export type Methods = {
4 | get: {
5 | resBody: Room[];
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/apps/server/api/index.ts:
--------------------------------------------------------------------------------
1 | export type Methods = {}
2 |
--------------------------------------------------------------------------------
/apps/server/api/lock/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 |
3 | export default defineController(() => ({}));
4 |
--------------------------------------------------------------------------------
/apps/server/api/lock/index.ts:
--------------------------------------------------------------------------------
1 | export type Methods = {}
2 |
--------------------------------------------------------------------------------
/apps/server/api/lock/rooms/_roomId@number/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { FindRoomUseCase } from '$/packages/application/usecase/lock/rooms/find';
4 | import { CheckoutUseCase } from '$/packages/application/usecase/lock/rooms/checkout';
5 |
6 | export default defineController(() => ({
7 | get: async({ params }) => container.resolve(FindRoomUseCase).execute(params.roomId),
8 | patch: async({ params }) => container.resolve(CheckoutUseCase).execute(params.roomId),
9 | }));
10 |
--------------------------------------------------------------------------------
/apps/server/api/lock/rooms/_roomId@number/index.ts:
--------------------------------------------------------------------------------
1 | import type { RoomWithValidReservation } from '$/packages/application/usecase/lock/rooms/find';
2 | import type { Reservation } from '$/packages/domain/database/reservation';
3 |
4 | export type Methods = {
5 | get: {
6 | resBody: RoomWithValidReservation;
7 | };
8 | patch: {
9 | resBody: Reservation;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/apps/server/api/lock/rooms/_roomId@number/keypad/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { ValidateKeyUseCase } from '$/packages/application/usecase/lock/rooms/validateKey';
4 |
5 | export default defineController(() => ({
6 | post: async({ body }) => container.resolve(ValidateKeyUseCase).execute(body.roomId, body.key),
7 | }));
8 |
--------------------------------------------------------------------------------
/apps/server/api/lock/rooms/_roomId@number/keypad/index.ts:
--------------------------------------------------------------------------------
1 | import type { RoomKeyBody } from '$/validators';
2 | import type { ValidateRoomKeyResult } from '$/packages/application/usecase/lock/rooms/validateKey';
3 |
4 | export type Methods = {
5 | post: {
6 | reqBody: RoomKeyBody;
7 | resBody: ValidateRoomKeyResult;
8 | };
9 | }
10 |
--------------------------------------------------------------------------------
/apps/server/api/lock/rooms/_roomId@number/qr/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { ValidateQrUseCase } from '$/packages/application/usecase/lock/rooms/validateQr';
4 |
5 | export default defineController(() => ({
6 | post: async({ body }) => container.resolve(ValidateQrUseCase).execute(body.roomId, body.data),
7 | }));
8 |
--------------------------------------------------------------------------------
/apps/server/api/lock/rooms/_roomId@number/qr/index.ts:
--------------------------------------------------------------------------------
1 | import type { RoomQrBody } from '$/validators';
2 | import type { ValidateRoomQrResult } from '$/packages/application/usecase/lock/rooms/validateQr';
3 |
4 | export type Methods = {
5 | post: {
6 | reqBody: RoomQrBody;
7 | resBody: ValidateRoomQrResult;
8 | };
9 | }
10 |
--------------------------------------------------------------------------------
/apps/server/api/lock/rooms/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { ListRoomsUseCase } from '$/packages/application/usecase/lock/rooms/list';
4 |
5 | export default defineController(() => ({
6 | get: async() => container.resolve(ListRoomsUseCase).execute(),
7 | }));
8 |
--------------------------------------------------------------------------------
/apps/server/api/lock/rooms/index.ts:
--------------------------------------------------------------------------------
1 | import type { Room } from '$/packages/domain/database/room';
2 |
3 | export type Methods = {
4 | get: {
5 | resBody: Room[];
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/apps/server/api/stripe/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 |
3 | export default defineController(() => ({}));
4 |
--------------------------------------------------------------------------------
/apps/server/api/stripe/hooks.ts:
--------------------------------------------------------------------------------
1 | import type { GuestAuthorizationPayload } from '$/packages/application/service/auth';
2 | import { defineHooks } from './$relay';
3 | import { verifyGuest } from '$/packages/application/service/auth';
4 |
5 | export type AdditionalRequest = {
6 | user: GuestAuthorizationPayload;
7 | }
8 |
9 | export default defineHooks(() => ({
10 | onRequest: async(request) => {
11 | await verifyGuest(request);
12 | },
13 | }));
14 |
--------------------------------------------------------------------------------
/apps/server/api/stripe/index.ts:
--------------------------------------------------------------------------------
1 | export type Methods = {}
2 |
--------------------------------------------------------------------------------
/apps/server/api/stripe/method/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { GetDefaultPaymentMethodUseCase } from '$/packages/application/usecase/stripe/getDefaultPaymentMethod';
4 |
5 | export default defineController(() => ({
6 | get: async({ user }) => container.resolve(GetDefaultPaymentMethodUseCase).execute(user.id),
7 | }));
8 |
--------------------------------------------------------------------------------
/apps/server/api/stripe/method/index.ts:
--------------------------------------------------------------------------------
1 | import type { AuthHeader } from '@frourio-demo/types';
2 |
3 | export type Methods = {
4 | get: {
5 | reqHeaders: AuthHeader;
6 | resBody: { id?: string };
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/apps/server/api/stripe/methods/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { GetPaymentMethodsUseCase } from '$/packages/application/usecase/stripe/getPaymentMethods';
4 | import { AttachPaymentMethodUseCase } from '$/packages/application/usecase/stripe/attachPaymentMethod';
5 | import { DetachPaymentMethodUseCase } from '$/packages/application/usecase/stripe/detachPaymentMethod';
6 |
7 | export default defineController(() => ({
8 | get: async({ user }) => container.resolve(GetPaymentMethodsUseCase).execute(user.id),
9 | put: async({ user, body }) => container.resolve(AttachPaymentMethodUseCase).execute(body.methodId, user.id),
10 | delete: async({ body }) => container.resolve(DetachPaymentMethodUseCase).execute(body.methodId),
11 | }));
12 |
--------------------------------------------------------------------------------
/apps/server/api/stripe/methods/index.ts:
--------------------------------------------------------------------------------
1 | import type { AuthHeader } from '@frourio-demo/types';
2 | import type { PaymentMethod } from '$/packages/domain/payment';
3 |
4 | export type Methods = {
5 | get: {
6 | reqHeaders: AuthHeader;
7 | resBody: PaymentMethod[]
8 | };
9 | put: {
10 | reqHeaders: AuthHeader;
11 | reqBody: { methodId: string }
12 | };
13 | delete: {
14 | reqHeaders: AuthHeader;
15 | reqBody: { methodId: string }
16 | };
17 | }
18 |
--------------------------------------------------------------------------------
/apps/server/api/webhook/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 |
3 | export default defineController(() => ({}));
4 |
--------------------------------------------------------------------------------
/apps/server/api/webhook/index.ts:
--------------------------------------------------------------------------------
1 | export type Methods = {}
2 |
--------------------------------------------------------------------------------
/apps/server/api/webhook/stripe/controller.ts:
--------------------------------------------------------------------------------
1 | import { defineController } from './$relay';
2 | import { container } from 'tsyringe';
3 | import { HandleWebhookUseCase } from '$/packages/application/usecase/stripe/handleWebhook';
4 |
5 | export default defineController(() => ({
6 | post: async({ body, headers }) => container.resolve(HandleWebhookUseCase).execute(body, headers['stripe-signature']),
7 | }));
8 |
--------------------------------------------------------------------------------
/apps/server/api/webhook/stripe/index.ts:
--------------------------------------------------------------------------------
1 | export type Methods = {
2 | post: {
3 | reqHeaders: { 'stripe-signature': string; };
4 | resBody: { received: boolean };
5 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
6 | reqBody: any;
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/apps/server/cron.ts:
--------------------------------------------------------------------------------
1 | import { schedule } from 'node-cron';
2 | import { SendRoomKeyUseCase } from '$/packages/application/usecase/front/reservation/sendRoomKey';
3 | import { CheckoutReservationsUseCase } from '$/packages/application/usecase/stripe/checkoutReservations';
4 | import { container } from 'tsyringe';
5 | import { logger } from '$/utils/logger';
6 |
7 | export const setup = () => {
8 | schedule('0 12 * * *', () => {
9 | logger.info('start send room key');
10 | container.resolve(SendRoomKeyUseCase).execute().finally(() => {
11 | logger.info('finish send room key');
12 | });
13 | }, {
14 | timezone: 'Asia/Tokyo',
15 | });
16 | schedule('30 12 * * *', () => {
17 | logger.info('start checkout check');
18 | container.resolve(CheckoutReservationsUseCase).execute().finally(() => {
19 | logger.info('finish checkout check');
20 | });
21 | }, {
22 | timezone: 'Asia/Tokyo',
23 | });
24 | };
25 |
--------------------------------------------------------------------------------
/apps/server/jest.setup.ts:
--------------------------------------------------------------------------------
1 | import 'reflect-metadata';
2 | jest.mock('bunyan', () => ({
3 | createLogger: () => ({
4 | debug: jest.fn(),
5 | error: jest.fn(),
6 | info: jest.fn(),
7 | }),
8 | }));
9 | jest.mock('@slack/webhook');
10 | export {};
--------------------------------------------------------------------------------
/apps/server/packages/application/service/auth0.ts:
--------------------------------------------------------------------------------
1 | import { depend } from 'velona';
2 | import fetch from 'node-fetch';
3 | import { auth0 } from '@frourio-demo/config';
4 |
5 | type Info = {
6 | email: string;
7 | auth0Sub: string;
8 | }
9 | export const verifyCode = depend(
10 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
11 | { fetch: fetch as (url: string, init: { headers: Record }) => Promise<{ ok: boolean; json: () => Promise }> },
12 | async({ fetch }, accessToken: string): Promise => {
13 | const response = await fetch(`https://${auth0.domain}/userinfo`, {
14 | headers: {
15 | Authorization: `Bearer ${accessToken}`,
16 | },
17 | });
18 |
19 | if (!response.ok) {
20 | return false;
21 | }
22 |
23 | const info = await response.json();
24 | return {
25 | email: info.email,
26 | auth0Sub: info.sub,
27 | };
28 | },
29 | );
30 |
--------------------------------------------------------------------------------
/apps/server/packages/application/service/pages.ts:
--------------------------------------------------------------------------------
1 | export const getSkip = (perPage: number, page: number): number => page * perPage;
2 | export const getCurrentPage = (perPage: number, totalCount: number, page: number): number => {
3 | const skip = getSkip(perPage, page);
4 | if (skip >= totalCount) {
5 | if (totalCount <= 0) {
6 | return 0;
7 | }
8 | return Math.floor((totalCount - 1) / perPage);
9 | }
10 |
11 | return page;
12 | };
13 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/admin/admins/create.ts:
--------------------------------------------------------------------------------
1 | import type { AdminBody } from './validators';
2 | import type { IAdminRepository } from '$/packages/domain/database/admin';
3 | import type { IResponseRepository } from '$/packages/domain/http/response';
4 | import { singleton, inject } from 'tsyringe';
5 | import { processBody } from './service';
6 |
7 | @singleton()
8 | export class CreateAdminUseCase {
9 | public constructor(@inject('IAdminRepository') private repository: IAdminRepository, @inject('IResponseRepository') private response: IResponseRepository) {
10 | }
11 |
12 | public async execute(data: AdminBody) {
13 | return this.response.created(await this.repository.create(await processBody(data, true)));
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/admin/admins/delete.ts:
--------------------------------------------------------------------------------
1 | import type { IAdminRepository } from '$/packages/domain/database/admin';
2 | import type { IResponseRepository } from '$/packages/domain/http/response';
3 | import { singleton, inject } from 'tsyringe';
4 |
5 | @singleton()
6 | export class DeleteAdminUseCase {
7 | public constructor(@inject('IAdminRepository') private repository: IAdminRepository, @inject('IResponseRepository') private response: IResponseRepository) {
8 | }
9 |
10 | public async execute(id: number) {
11 | return this.response.success(await this.repository.delete(id));
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/admin/admins/find.ts:
--------------------------------------------------------------------------------
1 | import type { IAdminRepository } from '$/packages/domain/database/admin';
2 | import type { IResponseRepository } from '$/packages/domain/http/response';
3 | import { singleton, inject } from 'tsyringe';
4 |
5 | @singleton()
6 | export class FindAdminUseCase {
7 | public constructor(@inject('IAdminRepository') private repository: IAdminRepository, @inject('IResponseRepository') private response: IResponseRepository) {
8 | }
9 |
10 | public async execute(id: number) {
11 | return this.response.success(await this.repository.find(id));
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/admin/admins/listRoles.ts:
--------------------------------------------------------------------------------
1 | import type { IRoleRepository } from '$/packages/domain/database/role';
2 | import type { IResponseRepository } from '$/packages/domain/http/response';
3 | import { singleton, inject } from 'tsyringe';
4 |
5 | export type ListRole = Record;
6 |
7 | @singleton()
8 | export class ListRolesUseCase {
9 | public constructor(@inject('IRoleRepository') private repository: IRoleRepository, @inject('IResponseRepository') private response: IResponseRepository) {
10 | }
11 |
12 | public async execute() {
13 | return this.response.success(Object.assign({}, ...(await this.repository.list()).map(role => ({ [role.role]: role.name }))));
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/admin/admins/searchRole.ts:
--------------------------------------------------------------------------------
1 | import type { Query } from '@technote-space/material-table';
2 | import type { Role } from '$/packages/domain/database/role';
3 | import type { IRoleRepository } from '$/packages/domain/database/role';
4 | import type { IResponseRepository } from '$/packages/domain/http/response';
5 | import { singleton, inject } from 'tsyringe';
6 | import { execute } from '$/packages/application/service/table';
7 |
8 | @singleton()
9 | export class SearchRoleUseCase {
10 | public constructor(@inject('IRoleRepository') private repository: IRoleRepository, @inject('IResponseRepository') private response: IResponseRepository) {
11 | }
12 |
13 | public async execute(query: Query) {
14 | return this.response.success(await execute(this.repository, query, ['name'], []));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/admin/admins/update.ts:
--------------------------------------------------------------------------------
1 | import type { AdminBody } from './validators';
2 | import type { IAdminRepository } from '$/packages/domain/database/admin';
3 | import type { IResponseRepository } from '$/packages/domain/http/response';
4 | import { singleton, inject } from 'tsyringe';
5 | import { processBody } from './service';
6 |
7 | @singleton()
8 | export class UpdateAdminUseCase {
9 | public constructor(@inject('IAdminRepository') private repository: IAdminRepository, @inject('IResponseRepository') private response: IResponseRepository) {
10 | }
11 |
12 | public async execute(id: number, data: AdminBody) {
13 | return this.response.success(await this.repository.update(id, await processBody(data, false)));
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/admin/admins/validators.ts:
--------------------------------------------------------------------------------
1 | import type Blob from 'cross-blob';
2 | import type { Role } from '$/packages/domain/database/role';
3 | import { IsNotEmpty, Length, IsEmail, MinLength, IsOptional } from 'class-validator';
4 | import { IsUniqueValue, IsOptionalWhenUpdate } from '$/packages/domain/database/service/validator';
5 | import { AdminRepository } from '$/packages/infra/database/admin';
6 |
7 | export class AdminBody {
8 | @IsNotEmpty({ message: '値を入力してください' })
9 | @Length(1, 100, { message: '1~100文字で入力してください' })
10 | name: string;
11 |
12 | @IsNotEmpty({ message: '値を入力してください' })
13 | @IsEmail(undefined, { message: '正しいメールアドレスを入力してください' })
14 | @IsUniqueValue(new AdminRepository())
15 | email: string;
16 |
17 | @IsOptional()
18 | @MinLength(4, { message: 'パスワードが短すぎます' })
19 | @IsOptionalWhenUpdate()
20 | password?: string;
21 |
22 | @IsOptional()
23 | icon?: Blob | string;
24 |
25 | @IsOptional()
26 | roles?: Role[];
27 | }
28 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/admin/dashboard/checkout.ts:
--------------------------------------------------------------------------------
1 | import type { IReservationRepository } from '$/packages/domain/database/reservation';
2 | import type { IResponseRepository } from '$/packages/domain/http/response';
3 | import { singleton, inject } from 'tsyringe';
4 |
5 | @singleton()
6 | export class CheckoutUseCase {
7 | public constructor(@inject('IReservationRepository') private repository: IReservationRepository, @inject('IResponseRepository') private response: IResponseRepository) {
8 | }
9 |
10 | public async execute(id: number) {
11 | const reservation = await this.repository.find(id);
12 | if (reservation.status === 'checkin') {
13 | return this.response.success(await this.repository.update(id, {
14 | status: 'checkout',
15 | }));
16 | }
17 |
18 | return this.response.badRequest('Not found or invalid status.');
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/admin/dashboard/getSelectableRooms.ts:
--------------------------------------------------------------------------------
1 | import type { Room } from '$/packages/domain/database/room';
2 | import type { IRoomRepository } from '$/packages/domain/database/room';
3 | import type { IResponseRepository } from '$/packages/domain/http/response';
4 | import { singleton, inject } from 'tsyringe';
5 |
6 | export type SelectableRoom = Pick;
7 |
8 | @singleton()
9 | export class GetSelectableRoomsUseCase {
10 | public constructor(@inject('IRoomRepository') private repository: IRoomRepository, @inject('IResponseRepository') private response: IResponseRepository) {
11 | }
12 |
13 | public async execute() {
14 | return this.response.success((await this.repository.list()).map(room => ({
15 | id: room.id,
16 | name: room.name,
17 | })));
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/admin/dashboard/types.ts:
--------------------------------------------------------------------------------
1 | export type MonthlySales = {
2 | month: Date;
3 | sales: number;
4 | }
5 |
6 | export type DailySales = {
7 | day: Date;
8 | sales: number;
9 | };
10 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/admin/dashboard/validators.ts:
--------------------------------------------------------------------------------
1 | import { IsPositive, IsInt } from 'class-validator';
2 | import { IsIdExists } from '$/packages/domain/database/service/validator';
3 | import { ReservationRepository } from '$/packages/infra/database/reservation';
4 |
5 | class ReservationStatusBody {
6 | @IsInt({ message: '整数値を指定してください' })
7 | @IsPositive({ message: '1以上を指定してください' })
8 | @IsIdExists(new ReservationRepository())
9 | id: number;
10 | }
11 |
12 | export class CheckinBody extends ReservationStatusBody {
13 | }
14 |
15 | export class SendRoomKeyBody extends ReservationStatusBody {
16 | }
17 |
18 | export class CheckoutBody extends ReservationStatusBody {
19 | }
20 |
21 | export class CancelBody extends ReservationStatusBody {
22 | }
23 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/admin/getAdmin.ts:
--------------------------------------------------------------------------------
1 | import type { IAdminRepository } from '$/packages/domain/database/admin';
2 | import type { IResponseRepository } from '$/packages/domain/http/response';
3 | import type { AdminAuthorizationPayload } from '$/packages/application/service/auth';
4 | import { singleton, inject } from 'tsyringe';
5 |
6 | @singleton()
7 | export class GetAdminUseCase {
8 | public constructor(@inject('IAdminRepository') private repository: IAdminRepository, @inject('IResponseRepository') private response: IResponseRepository) {
9 | }
10 |
11 | public async execute(user: AdminAuthorizationPayload) {
12 | return this.response.success(await this.repository.find(user.id));
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/admin/guests/create.ts:
--------------------------------------------------------------------------------
1 | import type { IGuestRepository, CreateGuestData } from '$/packages/domain/database/guest';
2 | import type { IResponseRepository } from '$/packages/domain/http/response';
3 | import { singleton, inject } from 'tsyringe';
4 |
5 | @singleton()
6 | export class CreateGuestUseCase {
7 | public constructor(@inject('IGuestRepository') private repository: IGuestRepository, @inject('IResponseRepository') private response: IResponseRepository) {
8 | }
9 |
10 | public async execute(data: CreateGuestData) {
11 | return this.response.created(await this.repository.create(data));
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/admin/guests/delete.ts:
--------------------------------------------------------------------------------
1 | import type { IGuestRepository } from '$/packages/domain/database/guest';
2 | import type { IResponseRepository } from '$/packages/domain/http/response';
3 | import { singleton, inject } from 'tsyringe';
4 |
5 | @singleton()
6 | export class DeleteGuestUseCase {
7 | public constructor(@inject('IGuestRepository') private repository: IGuestRepository, @inject('IResponseRepository') private response: IResponseRepository) {
8 | }
9 |
10 | public async execute(id: number) {
11 | return this.response.success(await this.repository.delete(id));
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/admin/guests/find.ts:
--------------------------------------------------------------------------------
1 | import type { IGuestRepository } from '$/packages/domain/database/guest';
2 | import type { IResponseRepository } from '$/packages/domain/http/response';
3 | import { singleton, inject } from 'tsyringe';
4 |
5 | @singleton()
6 | export class FindGuestUseCase {
7 | public constructor(@inject('IGuestRepository') private repository: IGuestRepository, @inject('IResponseRepository') private response: IResponseRepository) {
8 | }
9 |
10 | public async execute(id: number) {
11 | return this.response.success(await this.repository.find(id));
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/admin/guests/list.ts:
--------------------------------------------------------------------------------
1 | import type { Query } from '@technote-space/material-table';
2 | import type { Guest } from '$/packages/domain/database/guest';
3 | import type { IGuestRepository } from '$/packages/domain/database/guest';
4 | import type { IResponseRepository } from '$/packages/domain/http/response';
5 | import { singleton, inject } from 'tsyringe';
6 | import { execute } from '$/packages/application/service/table';
7 | import { ACCOUNT_FIELDS } from '@frourio-demo/constants';
8 |
9 | @singleton()
10 | export class ListGuestsUseCase {
11 | public constructor(@inject('IGuestRepository') private repository: IGuestRepository, @inject('IResponseRepository') private response: IResponseRepository) {
12 | }
13 |
14 | public async execute(query: Query) {
15 | return this.response.success(await execute(this.repository, query, [...ACCOUNT_FIELDS.map(field => field.name), 'auth0Sub', 'paymentId'], []));
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/admin/guests/update.ts:
--------------------------------------------------------------------------------
1 | import type { IGuestRepository, UpdateGuestData } from '$/packages/domain/database/guest';
2 | import type { IResponseRepository } from '$/packages/domain/http/response';
3 | import { singleton, inject } from 'tsyringe';
4 |
5 | @singleton()
6 | export class UpdateGuestUseCase {
7 | public constructor(@inject('IGuestRepository') private repository: IGuestRepository, @inject('IResponseRepository') private response: IResponseRepository) {
8 | }
9 |
10 | public async execute(id: number, data: UpdateGuestData) {
11 | return this.response.success(await this.repository.update(id, data));
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/admin/guests/validators.ts:
--------------------------------------------------------------------------------
1 | import { IsNotEmpty, IsEmail, Length } from 'class-validator';
2 | import { IsKatakana, IsZipCode, IsPhoneNumber } from '$/packages/domain/database/service/validator';
3 |
4 | export class GuestBody {
5 | @IsNotEmpty({ message: '値を入力してください' })
6 | @IsEmail(undefined, { message: 'メールアドレスを入力してください' })
7 | email: string;
8 |
9 | @IsNotEmpty({ message: '値を入力してください' })
10 | @Length(1, 100, { message: '1~100文字で入力してください' })
11 | name: string;
12 |
13 | @IsNotEmpty({ message: '値を入力してください' })
14 | @Length(1, 100, { message: '1~100文字で入力してください' })
15 | @IsKatakana()
16 | nameKana: string;
17 |
18 | @IsNotEmpty({ message: '値を入力してください' })
19 | @IsZipCode()
20 | zipCode: string;
21 |
22 | @IsNotEmpty({ message: '値を入力してください' })
23 | @Length(1, 255, { message: '1~255文字で入力してください' })
24 | address: string;
25 |
26 | @IsNotEmpty({ message: '値を入力してください' })
27 | @IsPhoneNumber()
28 | phone: string;
29 | }
30 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/admin/login/validators.ts:
--------------------------------------------------------------------------------
1 | import { MinLength, IsEmail } from 'class-validator';
2 |
3 | export class LoginBody {
4 | @IsEmail(undefined, { message: '正しいメールアドレスを入力してください' })
5 | email: string;
6 |
7 | @MinLength(4, { message: 'パスワードが短すぎます' })
8 | pass: string;
9 | }
10 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/admin/reservations/delete.ts:
--------------------------------------------------------------------------------
1 | import type { IReservationRepository } from '$/packages/domain/database/reservation';
2 | import type { IResponseRepository } from '$/packages/domain/http/response';
3 | import { singleton, inject } from 'tsyringe';
4 |
5 | @singleton()
6 | export class DeleteReservationUseCase {
7 | public constructor(@inject('IReservationRepository') private repository: IReservationRepository, @inject('IResponseRepository') private response: IResponseRepository) {
8 | }
9 |
10 | public async execute(id: number) {
11 | return this.response.success(await this.repository.delete(id));
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/admin/reservations/find.ts:
--------------------------------------------------------------------------------
1 | import type { IReservationRepository } from '$/packages/domain/database/reservation';
2 | import type { IResponseRepository } from '$/packages/domain/http/response';
3 | import { singleton, inject } from 'tsyringe';
4 |
5 | @singleton()
6 | export class FindReservationUseCase {
7 | public constructor(@inject('IReservationRepository') private repository: IReservationRepository, @inject('IResponseRepository') private response: IResponseRepository) {
8 | }
9 |
10 | public async execute(id: number) {
11 | return this.response.success(await this.repository.find(id));
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/admin/reservations/getSelectedGuest.ts:
--------------------------------------------------------------------------------
1 | import type { Guest, IGuestRepository } from '$/packages/domain/database/guest';
2 | import type { IResponseRepository } from '$/packages/domain/http/response';
3 | import { singleton, inject } from 'tsyringe';
4 |
5 | export type SelectedGuest = Pick;
6 |
7 | @singleton()
8 | export class GetSelectedGuestUseCase {
9 | public constructor(@inject('IGuestRepository') private repository: IGuestRepository, @inject('IResponseRepository') private response: IResponseRepository) {
10 | }
11 |
12 | public async execute(id: number) {
13 | const guest = await this.repository.find(id);
14 | return this.response.success({
15 | id: guest.id,
16 | name: guest.name,
17 | email: guest.email,
18 | });
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/admin/reservations/getSelectedRoom.ts:
--------------------------------------------------------------------------------
1 | import type { Room, IRoomRepository } from '$/packages/domain/database/room';
2 | import type { IResponseRepository } from '$/packages/domain/http/response';
3 | import { singleton, inject } from 'tsyringe';
4 |
5 | export type SelectedRoom = Pick;
6 |
7 | @singleton()
8 | export class GetSelectedRoomUseCase {
9 | public constructor(@inject('IRoomRepository') private repository: IRoomRepository, @inject('IResponseRepository') private response: IResponseRepository) {
10 | }
11 |
12 | public async execute(id: number) {
13 | const room = await this.repository.find(id);
14 |
15 | return this.response.success({
16 | id: room.id,
17 | name: room.name,
18 | number: room.number,
19 | price: room.price,
20 | });
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/admin/reservations/searchRoom.ts:
--------------------------------------------------------------------------------
1 | import type { Query } from '@technote-space/material-table';
2 | import type { Room } from '$/packages/domain/database/room';
3 | import type { IRoomRepository } from '$/packages/domain/database/room';
4 | import type { IResponseRepository } from '$/packages/domain/http/response';
5 | import { singleton, inject } from 'tsyringe';
6 | import { execute } from '$/packages/application/service/table';
7 |
8 | @singleton()
9 | export class SearchRoomUseCase {
10 | public constructor(@inject('IRoomRepository') private repository: IRoomRepository, @inject('IResponseRepository') private response: IResponseRepository) {
11 | }
12 |
13 | public async execute(query: Query) {
14 | return this.response.success(await execute(this.repository, query, ['name'], ['price', 'number']));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/admin/rooms/create.ts:
--------------------------------------------------------------------------------
1 | import type { IRoomRepository, CreateRoomData } from '$/packages/domain/database/room';
2 | import type { IResponseRepository } from '$/packages/domain/http/response';
3 | import { singleton, inject } from 'tsyringe';
4 |
5 | @singleton()
6 | export class CreateRoomUseCase {
7 | public constructor(@inject('IRoomRepository') private repository: IRoomRepository, @inject('IResponseRepository') private response: IResponseRepository) {
8 | }
9 |
10 | public async execute(data: CreateRoomData) {
11 | return this.response.created(await this.repository.create(data));
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/admin/rooms/delete.ts:
--------------------------------------------------------------------------------
1 | import type { IRoomRepository } from '$/packages/domain/database/room';
2 | import type { IResponseRepository } from '$/packages/domain/http/response';
3 | import { singleton, inject } from 'tsyringe';
4 |
5 | @singleton()
6 | export class DeleteRoomUseCase {
7 | public constructor(@inject('IRoomRepository') private repository: IRoomRepository, @inject('IResponseRepository') private response: IResponseRepository) {
8 | }
9 |
10 | public async execute(id: number) {
11 | return this.response.success(await this.repository.delete(id));
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/admin/rooms/find.ts:
--------------------------------------------------------------------------------
1 | import type { IRoomRepository } from '$/packages/domain/database/room';
2 | import type { IResponseRepository } from '$/packages/domain/http/response';
3 | import { singleton, inject } from 'tsyringe';
4 |
5 | @singleton()
6 | export class FindRoomUseCase {
7 | public constructor(@inject('IRoomRepository') private repository: IRoomRepository, @inject('IResponseRepository') private response: IResponseRepository) {
8 | }
9 |
10 | public async execute(id: number) {
11 | return this.response.success(await this.repository.find(id));
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/admin/rooms/list.ts:
--------------------------------------------------------------------------------
1 | import type { Query } from '@technote-space/material-table';
2 | import type { Room } from '$/packages/domain/database/room';
3 | import type { IRoomRepository } from '$/packages/domain/database/room';
4 | import type { IResponseRepository } from '$/packages/domain/http/response';
5 | import { singleton, inject } from 'tsyringe';
6 | import { execute } from '$/packages/application/service/table';
7 |
8 | @singleton()
9 | export class ListRoomsUseCase {
10 | public constructor(@inject('IRoomRepository') private repository: IRoomRepository, @inject('IResponseRepository') private response: IResponseRepository) {
11 | }
12 |
13 | public async execute(query: Query) {
14 | return this.response.success(await execute(this.repository, query, ['name'], ['price', 'number']));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/admin/rooms/update.ts:
--------------------------------------------------------------------------------
1 | import type { IRoomRepository, UpdateRoomData } from '$/packages/domain/database/room';
2 | import type { IResponseRepository } from '$/packages/domain/http/response';
3 | import { singleton, inject } from 'tsyringe';
4 |
5 | @singleton()
6 | export class UpdateRoomUseCase {
7 | public constructor(@inject('IRoomRepository') private repository: IRoomRepository, @inject('IResponseRepository') private response: IResponseRepository) {
8 | }
9 |
10 | public async execute(id: number, data: UpdateRoomData) {
11 | return this.response.success(await this.repository.update(id, data));
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/admin/rooms/validators.ts:
--------------------------------------------------------------------------------
1 | import { IsNotEmpty, Length, IsInt, IsPositive, Min } from 'class-validator';
2 |
3 | export class RoomBody {
4 | @IsNotEmpty({ message: '値を入力してください' })
5 | @Length(1, 100, { message: '1~100文字で入力してください' })
6 | name: string;
7 |
8 | @IsNotEmpty({ message: '値を入力してください' })
9 | @IsInt({ message: '整数値を指定してください' })
10 | @IsPositive({ message: '1以上を指定してください' })
11 | number: number;
12 |
13 | @IsNotEmpty({ message: '値を入力してください' })
14 | @IsInt({ message: '整数値を指定してください' })
15 | @Min(0, { message: '0以上を指定してください' })
16 | price: number;
17 | }
18 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/admin/validators.ts:
--------------------------------------------------------------------------------
1 | export * from './dashboard/validators';
2 | export * from './guests/validators';
3 | export * from './rooms/validators';
4 | export * from './reservations/validators';
5 | export * from './admins/validators';
6 | export * from './login/validators';
7 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/front/account/getCancelledReservations.ts:
--------------------------------------------------------------------------------
1 | import type { IReservationRepository } from '$/packages/domain/database/reservation';
2 | import type { IResponseRepository } from '$/packages/domain/http/response';
3 | import type { GuestAuthorizationPayload } from '$/packages/application/service/auth';
4 | import { singleton, inject } from 'tsyringe';
5 |
6 | @singleton()
7 | export class GetCancelledReservationsUseCase {
8 | public constructor(@inject('IReservationRepository') private repository: IReservationRepository, @inject('IResponseRepository') private response: IResponseRepository) {
9 | }
10 |
11 | public async execute(user: GuestAuthorizationPayload) {
12 | return this.response.success(await this.repository.list({
13 | where: {
14 | guestId: user.id,
15 | status: 'cancelled',
16 | },
17 | orderBy: {
18 | id: 'desc',
19 | },
20 | take: 20,
21 | }));
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/front/account/getGuestInfo.ts:
--------------------------------------------------------------------------------
1 | import type { IGuestRepository } from '$/packages/domain/database/guest';
2 | import type { IResponseRepository } from '$/packages/domain/http/response';
3 | import type { GuestAuthorizationPayload } from '$/packages/application/service/auth';
4 | import { singleton, inject } from 'tsyringe';
5 |
6 | @singleton()
7 | export class GetGuestInfoUseCase {
8 | public constructor(@inject('IGuestRepository') private repository: IGuestRepository, @inject('IResponseRepository') private response: IResponseRepository) {
9 | }
10 |
11 | public async execute(user: GuestAuthorizationPayload) {
12 | return this.response.success(await this.repository.find(user.id));
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/front/account/getPaidReservations.ts:
--------------------------------------------------------------------------------
1 | import type { IReservationRepository } from '$/packages/domain/database/reservation';
2 | import type { IResponseRepository } from '$/packages/domain/http/response';
3 | import type { GuestAuthorizationPayload } from '$/packages/application/service/auth';
4 | import { singleton, inject } from 'tsyringe';
5 |
6 | @singleton()
7 | export class GetPaidReservationsUseCase {
8 | public constructor(@inject('IReservationRepository') private repository: IReservationRepository, @inject('IResponseRepository') private response: IResponseRepository) {
9 | }
10 |
11 | public async execute(user: GuestAuthorizationPayload) {
12 | return this.response.success(await this.repository.list({
13 | where: {
14 | guestId: user.id,
15 | status: {
16 | in: ['checkin', 'checkout'],
17 | },
18 | },
19 | orderBy: {
20 | id: 'desc',
21 | },
22 | take: 20,
23 | }));
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/front/account/getReservedReservations.ts:
--------------------------------------------------------------------------------
1 | import type { IReservationRepository } from '$/packages/domain/database/reservation';
2 | import type { IResponseRepository } from '$/packages/domain/http/response';
3 | import type { GuestAuthorizationPayload } from '$/packages/application/service/auth';
4 | import { singleton, inject } from 'tsyringe';
5 |
6 | @singleton()
7 | export class GetReservedReservationsUseCase {
8 | public constructor(@inject('IReservationRepository') private repository: IReservationRepository, @inject('IResponseRepository') private response: IResponseRepository) {
9 | }
10 |
11 | public async execute(user: GuestAuthorizationPayload) {
12 | return this.response.success(await this.repository.list({
13 | where: {
14 | guestId: user.id,
15 | status: 'reserved',
16 | },
17 | orderBy: {
18 | id: 'desc',
19 | },
20 | }));
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/front/account/updateGuestInfo.ts:
--------------------------------------------------------------------------------
1 | import type { IGuestRepository } from '$/packages/domain/database/guest';
2 | import type { IResponseRepository } from '$/packages/domain/http/response';
3 | import type { GuestAuthorizationPayload } from '$/packages/application/service/auth';
4 | import type { UpdateGuestData } from '$/packages/domain/database/guest';
5 | import { singleton, inject } from 'tsyringe';
6 |
7 | @singleton()
8 | export class UpdateGuestInfoUseCase {
9 | public constructor(@inject('IGuestRepository') private repository: IGuestRepository, @inject('IResponseRepository') private response: IResponseRepository) {
10 | }
11 |
12 | public async execute(user: GuestAuthorizationPayload, data: UpdateGuestData) {
13 | return this.response.success(await this.repository.update(user.id, data));
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/front/account/validators.ts:
--------------------------------------------------------------------------------
1 | import { IsNotEmpty, IsEmail, Length } from 'class-validator';
2 | import { IsKatakana, IsZipCode, IsPhoneNumber } from '$/packages/domain/database/service/validator';
3 |
4 | export class AccountBody {
5 | @IsNotEmpty({ message: '値を入力してください' })
6 | @IsEmail(undefined, { message: 'メールアドレスを入力してください' })
7 | email: string;
8 |
9 | @IsNotEmpty({ message: '値を入力してください' })
10 | @Length(1, 100, { message: '1~100文字で入力してください' })
11 | name: string;
12 |
13 | @IsNotEmpty({ message: '値を入力してください' })
14 | @Length(1, 100, { message: '1~100文字で入力してください' })
15 | @IsKatakana()
16 | nameKana: string;
17 |
18 | @IsNotEmpty({ message: '値を入力してください' })
19 | @IsZipCode()
20 | zipCode: string;
21 |
22 | @IsNotEmpty({ message: '値を入力してください' })
23 | @Length(1, 255, { message: '1~255文字で入力してください' })
24 | address: string;
25 |
26 | @IsNotEmpty({ message: '値を入力してください' })
27 | @IsPhoneNumber()
28 | phone: string;
29 | }
30 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/front/guest/find.ts:
--------------------------------------------------------------------------------
1 | import type { IGuestRepository } from '$/packages/domain/database/guest';
2 | import type { IResponseRepository } from '$/packages/domain/http/response';
3 | import type { GuestAuthorizationPayload } from '$/packages/application/service/auth';
4 | import { singleton, inject } from 'tsyringe';
5 |
6 | @singleton()
7 | export class FindGuestUseCase {
8 | public constructor(@inject('IGuestRepository') private repository: IGuestRepository, @inject('IResponseRepository') private response: IResponseRepository) {
9 | }
10 |
11 | public async execute(user: GuestAuthorizationPayload) {
12 | return this.response.success(await this.repository.find(user.id));
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/front/login/validators.ts:
--------------------------------------------------------------------------------
1 | import { IsNotEmpty } from 'class-validator';
2 |
3 | export class Auth0Body {
4 | @IsNotEmpty()
5 | accessToken: string;
6 | }
7 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/front/reservation/getGuestInfo.ts:
--------------------------------------------------------------------------------
1 | import type { IGuestRepository } from '$/packages/domain/database/guest';
2 | import type { IResponseRepository } from '$/packages/domain/http/response';
3 | import type { GuestAuthorizationPayload } from '$/packages/application/service/auth';
4 | import { singleton, inject } from 'tsyringe';
5 |
6 | @singleton()
7 | export class GetGuestInfoUseCase {
8 | public constructor(@inject('IGuestRepository') private repository: IGuestRepository, @inject('IResponseRepository') private response: IResponseRepository) {
9 | }
10 |
11 | public async execute(user?: GuestAuthorizationPayload) {
12 | return this.response.success(user ? await this.repository.find(user.id) : undefined);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/front/reservation/getRoomInfo.ts:
--------------------------------------------------------------------------------
1 | import type { IRoomRepository } from '$/packages/domain/database/room';
2 | import type { IResponseRepository } from '$/packages/domain/http/response';
3 | import { singleton, inject } from 'tsyringe';
4 |
5 | @singleton()
6 | export class GetRoomInfoUseCase {
7 | public constructor(@inject('IRoomRepository') private repository: IRoomRepository, @inject('IResponseRepository') private response: IResponseRepository) {
8 | }
9 |
10 | public async execute(id: number) {
11 | return this.response.success(await this.repository.find(id));
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/front/reservation/getSelectRooms.ts:
--------------------------------------------------------------------------------
1 | import type { IRoomRepository } from '$/packages/domain/database/room';
2 | import type { IResponseRepository } from '$/packages/domain/http/response';
3 | import { singleton, inject } from 'tsyringe';
4 |
5 | @singleton()
6 | export class GetSelectRoomsUseCase {
7 | public constructor(@inject('IRoomRepository') private repository: IRoomRepository, @inject('IResponseRepository') private response: IResponseRepository) {
8 | }
9 |
10 | public async execute() {
11 | return this.response.success(await this.repository.list());
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/front/reservation/validate.ts:
--------------------------------------------------------------------------------
1 | import type { IResponseRepository } from '$/packages/domain/http/response';
2 | import { inject, singleton } from 'tsyringe';
3 |
4 | @singleton()
5 | export class ValidateUseCase {
6 | public constructor(@inject('IResponseRepository') private response: IResponseRepository) {
7 | }
8 |
9 | public async execute() {
10 | return this.response.success(undefined);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/front/rooms/find.ts:
--------------------------------------------------------------------------------
1 | import type { IRoomRepository } from '$/packages/domain/database/room';
2 | import type { IResponseRepository } from '$/packages/domain/http/response';
3 | import { singleton, inject } from 'tsyringe';
4 |
5 | @singleton()
6 | export class FindRoomUseCase {
7 | public constructor(@inject('IRoomRepository') private repository: IRoomRepository, @inject('IResponseRepository') private response: IResponseRepository) {
8 | }
9 |
10 | public async execute(id: number) {
11 | return this.response.success(await this.repository.find(id));
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/front/rooms/list.ts:
--------------------------------------------------------------------------------
1 | import type { IRoomRepository } from '$/packages/domain/database/room';
2 | import type { IResponseRepository } from '$/packages/domain/http/response';
3 | import { singleton, inject } from 'tsyringe';
4 |
5 | @singleton()
6 | export class ListRoomsUseCase {
7 | public constructor(@inject('IRoomRepository') private repository: IRoomRepository, @inject('IResponseRepository') private response: IResponseRepository) {
8 | }
9 |
10 | public async execute() {
11 | return this.response.success(await this.repository.list());
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/front/validators.ts:
--------------------------------------------------------------------------------
1 | export * from './account/validators';
2 | export * from './reservation/validators';
3 | export * from './login/validators';
4 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/lock/rooms/list.ts:
--------------------------------------------------------------------------------
1 | import type { IRoomRepository } from '$/packages/domain/database/room';
2 | import type { IResponseRepository } from '$/packages/domain/http/response';
3 | import { singleton, inject } from 'tsyringe';
4 |
5 | @singleton()
6 | export class ListRoomsUseCase {
7 | public constructor(@inject('IRoomRepository') private repository: IRoomRepository, @inject('IResponseRepository') private response: IResponseRepository) {
8 | }
9 |
10 | public async execute() {
11 | return this.response.success(await this.repository.list());
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/lock/rooms/validators.ts:
--------------------------------------------------------------------------------
1 | import { IsInt, IsPositive, IsNotEmpty } from 'class-validator';
2 | import { IsIdExists } from '$/packages/domain/database/service/validator';
3 | import { RoomRepository } from '$/packages/infra/database/room';
4 |
5 | export class RoomKeyBody {
6 | @IsNotEmpty({ message: '値を入力してください' })
7 | @IsInt({ message: '整数値を指定してください' })
8 | @IsPositive({ message: '1以上を指定してください' })
9 | @IsIdExists(new RoomRepository())
10 | roomId: number;
11 |
12 | @IsNotEmpty({ message: '値を入力してください' })
13 | key: string;
14 | }
15 |
16 | export class RoomQrBody {
17 | @IsNotEmpty({ message: '値を入力してください' })
18 | @IsInt({ message: '整数値を指定してください' })
19 | @IsPositive({ message: '1以上を指定してください' })
20 | @IsIdExists(new RoomRepository())
21 | roomId: number;
22 |
23 | @IsNotEmpty({ message: '値を入力してください' })
24 | data: string;
25 | }
26 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/lock/validators.ts:
--------------------------------------------------------------------------------
1 | export * from './rooms/validators';
2 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/stripe/detachPaymentMethod.ts:
--------------------------------------------------------------------------------
1 | import type { IResponseRepository } from '$/packages/domain/http/response';
2 | import type { IPaymentRepository } from '$/packages/domain/payment';
3 | import { singleton, inject } from 'tsyringe';
4 | import { logger } from '$/utils/logger';
5 |
6 | @singleton()
7 | export class DetachPaymentMethodUseCase {
8 | public constructor(
9 | @inject('IPaymentRepository') private payment: IPaymentRepository,
10 | @inject('IResponseRepository') private response: IResponseRepository,
11 | ) {
12 | }
13 |
14 | public async execute(id: string) {
15 | logger.info('detachPaymentMethod, id=%s', id);
16 | return this.response.success(await this.payment.detachPaymentMethod(id));
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/stripe/getDefaultPaymentMethod.ts:
--------------------------------------------------------------------------------
1 | import type { IGuestRepository } from '$/packages/domain/database/guest';
2 | import type { IResponseRepository } from '$/packages/domain/http/response';
3 | import type { IPaymentRepository } from '$/packages/domain/payment';
4 | import { singleton, inject } from 'tsyringe';
5 |
6 | @singleton()
7 | export class GetDefaultPaymentMethodUseCase {
8 | public constructor(
9 | @inject('IGuestRepository') private repository: IGuestRepository,
10 | @inject('IPaymentRepository') private payment: IPaymentRepository,
11 | @inject('IResponseRepository') private response: IResponseRepository,
12 | ) {
13 | }
14 |
15 | public async execute(guestId: number) {
16 | return this.response.success({
17 | id: await this.payment.getDefaultPaymentMethod(await this.repository.find(guestId)),
18 | });
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/stripe/getPaymentMethods.ts:
--------------------------------------------------------------------------------
1 | import type { IGuestRepository } from '$/packages/domain/database/guest';
2 | import type { IResponseRepository } from '$/packages/domain/http/response';
3 | import type { IPaymentRepository } from '$/packages/domain/payment';
4 | import { singleton, inject } from 'tsyringe';
5 |
6 | @singleton()
7 | export class GetPaymentMethodsUseCase {
8 | public constructor(
9 | @inject('IGuestRepository') private repository: IGuestRepository,
10 | @inject('IPaymentRepository') private payment: IPaymentRepository,
11 | @inject('IResponseRepository') private response: IResponseRepository,
12 | ) {
13 | }
14 |
15 | public async execute(guestId: number) {
16 | return this.response.success(await this.payment.listPaymentMethods(await this.repository.find(guestId)));
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/stripe/handleWebhook.ts:
--------------------------------------------------------------------------------
1 | import type { IResponseRepository } from '$/packages/domain/http/response';
2 | import type { IPaymentRepository } from '$/packages/domain/payment';
3 | import { singleton, inject } from 'tsyringe';
4 |
5 | @singleton()
6 | export class HandleWebhookUseCase {
7 | public constructor(
8 | @inject('IPaymentRepository') private payment: IPaymentRepository,
9 | @inject('IResponseRepository') private response: IResponseRepository,
10 | ) {
11 | }
12 |
13 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
14 | public async execute(body: any, sig: string) {
15 | return this.response.success(await this.payment.handleWebhook(body, sig));
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/apps/server/packages/application/usecase/validators.ts:
--------------------------------------------------------------------------------
1 | export * from './admin/validators';
2 | export * from './front/validators';
3 | export * from './lock/validators';
4 |
--------------------------------------------------------------------------------
/apps/server/packages/domain/database/role.ts:
--------------------------------------------------------------------------------
1 | import type { Prisma, Role } from '$/packages/domain/database/service/prisma';
2 |
3 | export type SearchRoleArgs = Prisma.RoleFindManyArgs;
4 | export type { Role };
5 |
6 | export interface IRoleRepository {
7 | list(args?: SearchRoleArgs): Promise;
8 |
9 | count(args?: Omit): Promise;
10 | }
11 |
--------------------------------------------------------------------------------
/apps/server/packages/domain/database/roomKey.ts:
--------------------------------------------------------------------------------
1 | import type { Prisma, RoomKey } from '$/packages/domain/database/service/prisma';
2 | import type { Reservation } from '$/packages/domain/database/reservation';
3 |
4 | export type FindRoomKeyArgs = Prisma.RoomKeyFindFirstArgs;
5 | export type CreateRoomKeyData = Omit & {
6 | key?: string;
7 | startAt?: Date | string;
8 | endAt?: Date | string;
9 | };
10 | export type CreateRoomKeyArgs = Prisma.RoomKeyCreateArgs;
11 | export type UpdateRoomKeyData = Prisma.RoomKeyUpdateInput;
12 | export type UpdateRoomKeyArgs = Prisma.RoomKeyUpdateArgs;
13 | export type { RoomKey };
14 |
15 | export interface IRoomKeyRepository {
16 | find(reservationId: number, args?: FindRoomKeyArgs): Promise;
17 |
18 | create(reservation: Reservation, data?: CreateRoomKeyData, args?: Partial): Promise;
19 |
20 | update(id: number, data: UpdateRoomKeyData, args?: Partial): Promise;
21 | }
22 |
--------------------------------------------------------------------------------
/apps/server/packages/domain/database/service/prisma.ts:
--------------------------------------------------------------------------------
1 | export * from '$/packages/infra/database/prisma/client';
2 |
--------------------------------------------------------------------------------
/apps/server/packages/domain/database/service/validatable.ts:
--------------------------------------------------------------------------------
1 | import { Models, Delegate } from './types';
2 |
3 | export interface IValidatable {
4 | getModelName(): Models;
5 |
6 | getDelegate(): Delegate;
7 | }
8 |
--------------------------------------------------------------------------------
/apps/server/packages/domain/mail/index.ts:
--------------------------------------------------------------------------------
1 | import type { Options } from 'nodemailer/lib/smtp-transport';
2 | import type { Address } from 'nodemailer/lib/mailer';
3 | import type { Reservation } from '$/packages/domain/database/reservation';
4 |
5 | export type MailOptions = Options;
6 | export type MailAddress = string | Address | Array;
7 | export type MailSettings = {
8 | from: string | Address;
9 | to: MailAddress;
10 | bcc?: MailAddress;
11 | subject: string;
12 | text: string;
13 | html: string;
14 | }
15 |
16 | export interface IMailRepository {
17 | sendReservedMail(reservation: Reservation): Promise;
18 |
19 | sendCancelledMail(reservation: Reservation): Promise;
20 |
21 | sendPaidMail(reservation: Reservation, paid: string): Promise;
22 |
23 | sendRoomKeyMail(reservation: Reservation, key: string, qr: string): Promise;
24 | }
25 |
--------------------------------------------------------------------------------
/apps/server/packages/infra/database/index.ts:
--------------------------------------------------------------------------------
1 | import { PrismaClient } from './prisma/client';
2 |
3 | export const prisma = new PrismaClient();
4 |
--------------------------------------------------------------------------------
/apps/server/packages/infra/database/prisma/.env.example:
--------------------------------------------------------------------------------
1 | DATABASE_URL="mysql://frourio-demo:test1234@localhost:13306/frourio-demo"
2 |
--------------------------------------------------------------------------------
/apps/server/packages/infra/database/prisma/.gitignore:
--------------------------------------------------------------------------------
1 | /client/
2 | *-journal
--------------------------------------------------------------------------------
/apps/server/packages/infra/database/prisma/client.ts:
--------------------------------------------------------------------------------
1 | export * from './client/index';
2 |
--------------------------------------------------------------------------------
/apps/server/packages/infra/database/prisma/migrations/migration_lock.toml:
--------------------------------------------------------------------------------
1 | # Please do not edit this file manually
2 | # It should be added in your version-control system (i.e. Git)
3 | provider = "mysql"
--------------------------------------------------------------------------------
/apps/server/packages/infra/database/prisma/seed.ts:
--------------------------------------------------------------------------------
1 | import { runner } from './runner';
2 |
3 | (async() => {
4 | await runner.run();
5 | })();
6 |
--------------------------------------------------------------------------------
/apps/server/packages/infra/database/prisma/seeder/guest.ts:
--------------------------------------------------------------------------------
1 | import type { PrismaClient } from '../client';
2 | import { Seeder } from '@technote-space/prisma-seeder-tools/dist/seeder';
3 |
4 | export class GuestSeeder extends Seeder {
5 | constructor(prisma: PrismaClient) {
6 | super(prisma);
7 | }
8 |
9 | public async run(): Promise {
10 | await this.factory('guest').createMany(10);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/apps/server/packages/infra/database/prisma/seeder/reservation.ts:
--------------------------------------------------------------------------------
1 | import type { PrismaClient } from '../client';
2 | import { Seeder } from '@technote-space/prisma-seeder-tools/dist/seeder';
3 |
4 | export class ReservationSeeder extends Seeder {
5 | constructor(prisma: PrismaClient) {
6 | super(prisma);
7 | }
8 |
9 | public async run(): Promise {
10 | const rooms = await this.factory('room').list();
11 | const guests = await this.factory('guest').list();
12 | await [...Array(300)].reduce(async prev => {
13 | await prev;
14 | await this.factory('reservation').create({}, rooms.random(), guests.random());
15 | }, Promise.resolve());
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/apps/server/packages/infra/database/prisma/seeder/role.ts:
--------------------------------------------------------------------------------
1 | import type { PrismaClient } from '../client';
2 | import { Seeder } from '@technote-space/prisma-seeder-tools/dist/seeder';
3 | import { getRolesValue } from '$/packages/application/service/auth';
4 |
5 | export class RoleSeeder extends Seeder {
6 | constructor(prisma: PrismaClient) {
7 | super(prisma);
8 | }
9 |
10 | public async run(): Promise {
11 | await getRolesValue([
12 | { domain: 'dashboard', targets: ['create', 'read', 'update', 'delete'] },
13 | { domain: 'admins', targets: ['create', 'read', 'update', 'delete'] },
14 | { domain: 'rooms', targets: ['create', 'read', 'update', 'delete'] },
15 | { domain: 'guests', targets: ['create', 'read', 'update', 'delete'] },
16 | { domain: 'reservations', targets: ['create', 'read', 'update', 'delete'] },
17 | ]).reduce(async(prev, role) => {
18 | await prev;
19 | await this.factory('role').create(role);
20 | }, Promise.resolve());
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/apps/server/packages/infra/database/prisma/seeder/room.ts:
--------------------------------------------------------------------------------
1 | import type { PrismaClient } from '../client';
2 | import { Seeder } from '@technote-space/prisma-seeder-tools/dist/seeder';
3 |
4 | export class RoomSeeder extends Seeder {
5 | constructor(prisma: PrismaClient) {
6 | super(prisma);
7 | }
8 |
9 | public async run(): Promise {
10 | await this.factory('room').createMany(5);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/apps/server/packages/infra/database/role.ts:
--------------------------------------------------------------------------------
1 | import type { IRoleRepository, SearchRoleArgs } from '$/packages/domain/database/role';
2 | import { depend } from 'velona';
3 | import { prisma } from '$/packages/infra/database';
4 |
5 | export class RoleRepository implements IRoleRepository {
6 | list = depend(
7 | { prisma: prisma as { role: { findMany: typeof prisma.role.findMany } } },
8 | async({ prisma }, args?: SearchRoleArgs) => prisma.role.findMany(args),
9 | );
10 |
11 | count = depend(
12 | { prisma: prisma as { role: { count: typeof prisma.role.count } } },
13 | async({ prisma }, args?: Omit) => prisma.role.count(args),
14 | );
15 | }
16 |
--------------------------------------------------------------------------------
/apps/server/public/icons/dummy.svg:
--------------------------------------------------------------------------------
1 |
13 |
--------------------------------------------------------------------------------
/apps/server/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "noEmit": false
5 | },
6 | "ts-node": {
7 | "compilerOptions": {
8 | "module": "commonjs",
9 | "isolatedModules": false
10 | }
11 | },
12 | "exclude": [
13 | "node_modules"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/apps/server/types/html.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.html' {
2 | const content: string;
3 | export default content;
4 | }
5 |
--------------------------------------------------------------------------------
/apps/server/utils/slack.ts:
--------------------------------------------------------------------------------
1 | import type { IncomingWebhookSendArguments } from '@slack/webhook';
2 | import { IncomingWebhook } from '@slack/webhook';
3 | import { SLACK_WEBHOOK_URL } from '$/config/env';
4 |
5 | export const sendError = async(error: Error) => send({
6 | username: 'Slack Bot',
7 | text: error.message,
8 | 'icon_emoji': 'no_entry',
9 | attachments: [{
10 | color: 'danger',
11 | text: error.stack,
12 | }],
13 | });
14 |
15 | export const sendOk = async(text: string, fields?: { title: string; value: string; short?: boolean }[]) => send({
16 | username: 'Slack Bot',
17 | text,
18 | attachments: [{
19 | color: 'good',
20 | fields,
21 | }],
22 | });
23 |
24 | export const send = async(args: IncomingWebhookSendArguments) => {
25 | if (!SLACK_WEBHOOK_URL) {
26 | return;
27 | }
28 |
29 | const webhook = new IncomingWebhook(SLACK_WEBHOOK_URL);
30 | return webhook.send(args);
31 | };
32 |
--------------------------------------------------------------------------------
/apps/server/validators/index.ts:
--------------------------------------------------------------------------------
1 | export * from '$/packages/application/usecase/validators';
2 |
--------------------------------------------------------------------------------
/config/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .next
3 | dist
4 | out
5 | **/server/migration
6 | **/server/index.js
7 | **/server/tasks
8 | **/server/**/prisma/client
9 |
--------------------------------------------------------------------------------
/config/aspida.config.js:
--------------------------------------------------------------------------------
1 | require('dotenv').config({ path: 'apps/server/.env' });
2 |
3 | module.exports = {
4 | input: 'apps/server/api',
5 | baseURL: `${process.env.API_ORIGIN || ''}${!process.env.SERVER_PORT || process.env.SERVER_PORT === '80' ? '' : `:${process.env.SERVER_PORT}`}${process.env.BASE_PATH || ''}`,
6 | };
7 |
--------------------------------------------------------------------------------
/config/jest.global.setup.ts:
--------------------------------------------------------------------------------
1 | const setup = async() => {
2 | process.env.TZ = 'Asia/Tokyo';
3 | };
4 | export default setup;
5 |
--------------------------------------------------------------------------------
/config/jest.setup.ts:
--------------------------------------------------------------------------------
1 | jest.retryTimes(10);
2 | jest.setTimeout(60000);
3 | export {};
--------------------------------------------------------------------------------
/config/license-format.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "",
3 | "version": "",
4 | "repository": "",
5 | "licenses": "",
6 | "licenseText": "",
7 | "path": false,
8 | "publisher": false,
9 | "licenseFile": false,
10 | "copyright": false,
11 | "email": false,
12 | "url": false
13 | }
--------------------------------------------------------------------------------
/gh-pages/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/technote-space/frourio-demo/8a0e82e8347375953cc9c857ed8777331f50afdb/gh-pages/.nojekyll
--------------------------------------------------------------------------------
/infra/.env.example:
--------------------------------------------------------------------------------
1 | DB_NAME=frourio-demo
2 | DB_USER=frourio-demo
3 | DB_PASS=test1234
4 | DB_PORT=13306
5 | TZ=Asia/Tokyo
6 |
--------------------------------------------------------------------------------
/infra/README.md:
--------------------------------------------------------------------------------
1 | # 開発用
2 |
3 | ## セットアップ
4 |
5 | プロジェクトルートに移動し以下を実行
6 |
7 | ```shell
8 | make setup
9 | ```
10 |
11 | 環境変数の用意と [NVM](https://github.com/nvm-sh/nvm) のインストール
12 |
13 | ## スタート
14 |
15 | ```shell
16 | make up
17 | ```
18 |
19 | * MySQL
20 | * MailDev
21 |
22 | Docker Compose が動作する環境が必要
23 | [Docker Compose のインストール](https://docs.docker.jp/compose/install.html#docker-compose)
24 |
25 | ### メール
26 |
27 | [MailDev](https://github.com/maildev/maildev) で送信されたメールをキャッチ
28 | http://localhost:1080/
29 | で閲覧可能
30 |
31 | ## ストップ
32 |
33 | ```shell
34 | make down
35 | ```
36 |
--------------------------------------------------------------------------------
/infra/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 |
3 | services:
4 | db:
5 | image: mysql:8.0
6 | volumes:
7 | - frourio_demo_db:/var/lib/mysql
8 | - ../logs:/var/log/mysql
9 | - ./docker/mysql/conf.d/my.cnf:/etc/mysql/conf.d/my.cnf
10 | - ./docker/mysql/entrypoint:/docker-entrypoint-initdb.d
11 | ports:
12 | - ${DB_PORT}:3306
13 | restart: always
14 | environment:
15 | - MYSQL_DATABASE=${DB_NAME}
16 | - MYSQL_USER=${DB_USER}
17 | - MYSQL_PASSWORD=${DB_PASS}
18 | - MYSQL_ROOT_PASSWORD=${DB_PASS}
19 | - TZ=${TZ}
20 |
21 | maildev:
22 | image: maildev/maildev
23 | ports:
24 | - "1080:80"
25 | - "1025:25"
26 |
27 | volumes:
28 | frourio_demo_db:
29 |
--------------------------------------------------------------------------------
/infra/docker/mysql/conf.d/my.cnf:
--------------------------------------------------------------------------------
1 | # MySQLサーバーへの設定
2 | [mysqld]
3 | # 文字コード/照合順序の設定
4 | character-set-server = utf8mb4
5 | collation-server = utf8mb4_bin
6 |
7 | # タイムゾーンの設定
8 | default-time-zone = SYSTEM
9 | log_timestamps = SYSTEM
10 |
11 | # デフォルト認証プラグインの設定
12 | default-authentication-plugin = mysql_native_password
13 |
14 | # エラーログの設定
15 | log-error = /var/log/mysql/mysql-error.log
16 |
17 | # スロークエリログの設定
18 | slow_query_log = 1
19 | slow_query_log_file = /var/log/mysql/mysql-slow.log
20 | long_query_time = 5.0
21 | log_queries_not_using_indexes = 0
22 |
23 | # 実行ログの設定
24 | general_log = 1
25 | general_log_file = /var/log/mysql/mysql-query.log
26 |
27 | # mysqlオプションの設定
28 | [mysql]
29 | # 文字コードの設定
30 | default-character-set = utf8mb4
31 |
32 | # mysqlクライアントツールの設定
33 | [client]
34 | # 文字コードの設定
35 | default-character-set = utf8mb4
36 |
--------------------------------------------------------------------------------
/infra/docker/mysql/entrypoint/init.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | mysql=( mysql --protocol=socket -uroot -hlocalhost -p"${MYSQL_ROOT_PASSWORD}" )
6 | echo "GRANT CREATE, ALTER, DROP, REFERENCES ON *.* TO '"$MYSQL_USER"'@'%' ;" | "${mysql[@]}"
7 | echo 'FLUSH PRIVILEGES ;' | "${mysql[@]}"
8 |
--------------------------------------------------------------------------------
/shared/config/auth0.json:
--------------------------------------------------------------------------------
1 | {
2 | "domain": "technote-space.jp.auth0.com",
3 | "clientId": "CRRgx2qfEVBR4tDUQB9cKx9VyW6x2sOv"
4 | }
--------------------------------------------------------------------------------
/shared/config/index.ts:
--------------------------------------------------------------------------------
1 | import auth0 from './auth0.json';
2 |
3 | export { auth0 };
4 |
--------------------------------------------------------------------------------
/shared/config/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@frourio-demo/config",
3 | "version": "0.1.0",
4 | "private": true,
5 | "license": "MIT",
6 | "scripts": {
7 | "update": "npm_config_yes=true npx npm-check-updates -u --timeout 100000"
8 | },
9 | "dependencies": {},
10 | "devDependencies": {
11 | "typescript": "^4.3.5"
12 | },
13 | "engines": {
14 | "yarn": "1.x"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/shared/config/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": [
4 | "**/*.ts"
5 | ],
6 | "exclude": [
7 | "node_modules"
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/shared/constants/index.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-explicit-any */
2 |
3 | export const ACCOUNT_FIELDS = [
4 | { name: 'email', label: 'メールアドレス' },
5 | { name: 'name', label: '名前' },
6 | { name: 'nameKana', label: 'かな名' },
7 | { name: 'zipCode', label: '郵便番号' },
8 | { name: 'address', label: '住所' },
9 | { name: 'phone', label: '電話番号' },
10 | ] as const;
11 | export const RESERVATION_GUEST_FIELDS = ['email', 'name', 'nameKana', 'zipCode', 'address', 'phone'] as const;
12 | export const RESERVATION_STATUS = {
13 | reserved: '予約済み',
14 | cancelled: 'キャンセル',
15 | checkin: 'チェックイン',
16 | checkout: 'チェックアウト',
17 | paymentFailed: '支払い失敗',
18 | } as const;
19 |
20 | export const ROOM_KEY_DIGITS = 6;
21 | export const MAX_TRIALS = 3;
22 | export const CANCEL_PAYMENT_RATE = 0.8;
23 |
--------------------------------------------------------------------------------
/shared/constants/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@frourio-demo/constants",
3 | "version": "0.1.0",
4 | "private": true,
5 | "license": "MIT",
6 | "scripts": {
7 | "update": "npm_config_yes=true npx npm-check-updates -u --timeout 100000"
8 | },
9 | "dependencies": {},
10 | "devDependencies": {
11 | "typescript": "^4.3.5"
12 | },
13 | "engines": {
14 | "yarn": "1.x"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/shared/constants/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": [
4 | "**/*.ts"
5 | ],
6 | "exclude": [
7 | "node_modules"
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/shared/types/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@frourio-demo/types",
3 | "version": "0.1.0",
4 | "private": true,
5 | "license": "MIT",
6 | "scripts": {
7 | "update": "npm_config_yes=true npx npm-check-updates -u --timeout 100000"
8 | },
9 | "dependencies": {},
10 | "devDependencies": {
11 | "@types/react": "^17.0.14",
12 | "aspida": "^1.7.1",
13 | "react": "^17.0.2",
14 | "typescript": "^4.3.5"
15 | },
16 | "engines": {
17 | "yarn": "1.x"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/shared/types/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": [
4 | "**/*.ts"
5 | ],
6 | "exclude": [
7 | "node_modules"
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/shared/utils/api.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-explicit-any */
2 | import type { AxiosError } from 'axios';
3 |
4 | export const isAxiosError = (target: any): target is AxiosError => {
5 | return typeof target === 'object' &&
6 | 'config' in target &&
7 | 'request' in target &&
8 | 'response' in target;
9 | };
10 |
11 | export const processUpdateData = >(data: T): Omit => {
15 | delete data.updatedAt;
16 | delete data.createdAt;
17 | return data;
18 | };
19 |
--------------------------------------------------------------------------------
/shared/utils/calc.ts:
--------------------------------------------------------------------------------
1 | import { differenceInCalendarDays } from 'date-fns';
2 |
3 | export const getNights = (checkin: string | Date, checkout: string | Date): number => differenceInCalendarDays(new Date(checkout), new Date(checkin));
4 |
5 | export const getPriceCalc = (price: number, number: number, checkin: string | Date, checkout: string | Date, amount: number): string => {
6 | const nights = getNights(checkin, checkout);
7 | if (nights <= 0) {
8 | return '';
9 | }
10 |
11 | const calculated = price * number * nights;
12 | return (amount === calculated ? '' : `¥${calculated} = `) + `¥${price} * ${number}人 * ${nights}泊`;
13 | };
14 |
--------------------------------------------------------------------------------
/shared/utils/calendar.ts:
--------------------------------------------------------------------------------
1 | import { eachDayOfInterval, subDays, format } from 'date-fns';
2 |
3 | type EventApi = { start: Date; end: Date; }
4 | type FullCalendar = { getApi: () => ({ getEvents: () => { start: Date | null, end: Date | null }[] | undefined }) }
5 | export const getEventDates = (calendar: FullCalendar | null): Array =>
6 | [...new Set((calendar?.getApi().getEvents() ?? []).filter(
7 | (event): event is EventApi => !!event.start && !!event.end,
8 | ).flatMap(event => eachDayOfInterval({
9 | start: event.start,
10 | end: subDays(event.end, 1),
11 | })).map(date => format(date, 'yyyy-MM-dd')))].sort();
12 |
--------------------------------------------------------------------------------
/shared/utils/component.ts:
--------------------------------------------------------------------------------
1 | import type { FC } from 'react';
2 |
3 | export const getDisplayName = (prefix: string, Component: T) => `${prefix}(${Component.displayName || Component.name || 'Component'})`;
4 | export const addDisplayName = (prefix: string, Component: T, WrappedComponent?: U) => {
5 | Component.displayName = getDisplayName(prefix, WrappedComponent ?? Component);
6 | return Component;
7 | };
8 |
--------------------------------------------------------------------------------
/shared/utils/license.ts:
--------------------------------------------------------------------------------
1 | import type { License, RawLicenseType } from '@/types';
2 |
3 | export const ensureString = (value: string | Array): string => typeof value === 'string' ? value : value.join(', ');
4 | export const processLicenses = (licenses: Record): Array => Object.values(licenses).map(license => ({
5 | name: license.name,
6 | version: license.version,
7 | licenses: ensureString(license.licenses),
8 | repository: license.repository,
9 | licenseText: license.licenseText,
10 | }));
11 |
--------------------------------------------------------------------------------
/shared/utils/misc.ts:
--------------------------------------------------------------------------------
1 | export const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
2 |
--------------------------------------------------------------------------------
/shared/utils/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@frourio-demo/utils",
3 | "version": "0.1.0",
4 | "private": true,
5 | "license": "MIT",
6 | "scripts": {
7 | "update": "npm_config_yes=true npx npm-check-updates -u --timeout 100000"
8 | },
9 | "dependencies": {
10 | "@technote-space/imi-moji-converter": "^0.1.12"
11 | },
12 | "devDependencies": {
13 | "@types/react": "^17.0.14",
14 | "axios": "^0.21.1",
15 | "date-fns": "^2.22.1",
16 | "react": "^17.0.2",
17 | "typescript": "^4.3.5"
18 | },
19 | "engines": {
20 | "yarn": "1.x"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/shared/utils/string.ts:
--------------------------------------------------------------------------------
1 | import type { Primitive } from '@frourio-demo/types';
2 |
3 | export const startWithUppercase = (value: string): string => value.length ? value.charAt(0).toUpperCase() + value.slice(1) : '';
4 |
5 | const replaceAll = (string: string, key: string, value: Primitive) => string.split(key).join(`${value ?? ''}`);
6 | export const replaceVariables = (string: string, variables: Record) =>
7 | Object.keys(variables).reduce((acc, key) => replaceAll(acc, `\${${key}}`, variables[key]), string);
8 |
--------------------------------------------------------------------------------
/shared/utils/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": [
4 | "**/*.ts"
5 | ],
6 | "exclude": [
7 | "node_modules"
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------