diff --git a/dictation_client/src/App.tsx b/dictation_client/src/App.tsx index 25ff190..122ecc1 100644 --- a/dictation_client/src/App.tsx +++ b/dictation_client/src/App.tsx @@ -13,6 +13,7 @@ import "./styles/GlobalStyle.css"; const App = (): JSX.Element => { const dispatch = useDispatch(); const { instance } = useMsal(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars const [t, i18n] = useTranslation(); const pca = new PublicClientApplication(msalConfig); useEffect(() => { diff --git a/dictation_client/src/AppRouter.tsx b/dictation_client/src/AppRouter.tsx index 78abe59..6a3fe33 100644 --- a/dictation_client/src/AppRouter.tsx +++ b/dictation_client/src/AppRouter.tsx @@ -11,10 +11,31 @@ const AppRouter: React.FC = () => ( } /> } /> - } /> + } /> } /> } />} + /> + {/* XXX ヘッダーの挙動確認のため仮のページを作成 */} + } />} + /> + } />} + /> + } />} + /> + } />} + /> + } />} /> } /> diff --git a/dictation_client/src/components/footer/footer.module.scss b/dictation_client/src/components/footer/footer.module.scss new file mode 100644 index 0000000..51a89db --- /dev/null +++ b/dictation_client/src/components/footer/footer.module.scss @@ -0,0 +1,10 @@ +.footer { + grid-area: footer; + padding: 0.6rem 0; + text-align: center; + font-size: 11px; + line-height: 1.45; + letter-spacing: 0.4px; + font-weight: normal; + color: #999999; +} diff --git a/dictation_client/src/components/footer/footer.module.scss.d.ts b/dictation_client/src/components/footer/footer.module.scss.d.ts new file mode 100644 index 0000000..de4fd16 --- /dev/null +++ b/dictation_client/src/components/footer/footer.module.scss.d.ts @@ -0,0 +1,4 @@ +declare const classNames: { + readonly footer: "footer"; +}; +export = classNames; diff --git a/dictation_client/src/components/footer/index.tsx b/dictation_client/src/components/footer/index.tsx new file mode 100644 index 0000000..360b9a9 --- /dev/null +++ b/dictation_client/src/components/footer/index.tsx @@ -0,0 +1,10 @@ +import React from "react"; +import styles from "./footer.module.scss"; + +const Footer: React.FC = () => ( +
+
© OM Digital Solutions 2023
+
+); + +export default Footer; diff --git a/dictation_client/src/components/header/constants.ts b/dictation_client/src/components/header/constants.ts new file mode 100644 index 0000000..fe99f60 --- /dev/null +++ b/dictation_client/src/components/header/constants.ts @@ -0,0 +1,12 @@ +import { HeaderMenus, LoginedPaths } from "./types"; + +export const HEADER_MENUS: { label: HeaderMenus; path: LoginedPaths }[] = [ + { label: "Account", path: "/account" }, + { label: "Dictations", path: "/dictations" }, + { label: "License", path: "/license" }, + { label: "User", path: "/user" }, + { label: "Workflow", path: "/workflow" }, + { label: "XXX", path: "/xxx" }, // XXX 仮のタブ +]; + +export const HEADER_NAME = "ODMS Cloud"; diff --git a/dictation_client/src/components/header/header.module.scss b/dictation_client/src/components/header/header.module.scss new file mode 100644 index 0000000..66872f2 --- /dev/null +++ b/dictation_client/src/components/header/header.module.scss @@ -0,0 +1,47 @@ +.header { + grid-area: header; + position: relative; + display: flex; + flex-wrap: wrap; + justify-content: space-between; + box-shadow: 0 0 3px #282828; + background: linear-gradient(#ffffff, #ffffff 70%, #f0f0f0 100%); + z-index: 4; +} +.headerLogo { + margin: 1.8rem 2rem 1rem; + font-size: 1.71rem; + line-height: 2rem; + letter-spacing: 0.07rem; + font-weight: normal; +} +.headerLogo img { + width: 198px; +} +.headerSub { + margin: 1.8rem 2rem 1rem; + font-size: 1.2rem; + line-height: 2rem; + letter-spacing: 0.07rem; + font-weight: normal; + font-weight: 600; + text-align: right; +} + +@media only screen and (min-width: 1280px) { + .header.home { + display: block; + padding-top: 30vh; + padding-left: 40%; + background: none; + box-shadow: none; + } + + .header.home .headerSub { + font-size: 1.4rem; + line-height: 2rem; + letter-spacing: 0.07rem; + font-weight: 600; + text-align: left; + } +} diff --git a/dictation_client/src/components/header/header.module.scss.d.ts b/dictation_client/src/components/header/header.module.scss.d.ts new file mode 100644 index 0000000..772a72d --- /dev/null +++ b/dictation_client/src/components/header/header.module.scss.d.ts @@ -0,0 +1,7 @@ +declare const classNames: { + readonly header: "header"; + readonly headerLogo: "headerLogo"; + readonly headerSub: "headerSub"; + readonly home: "home"; +}; +export = classNames; diff --git a/dictation_client/src/components/header/index.tsx b/dictation_client/src/components/header/index.tsx new file mode 100644 index 0000000..150c3c5 --- /dev/null +++ b/dictation_client/src/components/header/index.tsx @@ -0,0 +1,25 @@ +import React from "react"; +import { useLocation } from "react-router-dom"; +import LoginedHeader from "./loginedHeader"; +import NotLoginHeader from "./notLoginHeader"; +import { isLoginPaths } from "./utils"; + +interface HeaderProps { + userName?: string; + // userRole: string; ログインユーザーのロールに応じてタブの活性非活性に使用する想定 +} +// ヘッダー切り替え用のcomponent +const Header: React.FC = (props) => { + const { userName } = props; + const location = useLocation(); + return getHeader(location.pathname, userName); +}; + +export default Header; + +const getHeader = (path: string, userName?: string) => { + if (isLoginPaths(path) && userName) { + return ; + } + return ; +}; diff --git a/dictation_client/src/components/header/loginedHeader.module.scss b/dictation_client/src/components/header/loginedHeader.module.scss new file mode 100644 index 0000000..b807ee1 --- /dev/null +++ b/dictation_client/src/components/header/loginedHeader.module.scss @@ -0,0 +1,87 @@ +.header { + grid-area: header; + position: relative; + display: flex; + flex-wrap: wrap; + justify-content: space-between; + box-shadow: 0 0 3px #282828; + background: linear-gradient(#ffffff, #ffffff 70%, #f0f0f0 100%); + z-index: 4; +} +.headerLogo { + margin: 1.2rem 2rem 1rem; + font-size: 1.71rem; + line-height: 2rem; + letter-spacing: 0.07rem; + font-weight: normal; +} +.headerLogo img { + width: 198px; +} +.headerSub { + margin: 1.4rem 2rem 1rem; + font-size: 1.2rem; + line-height: 2rem; + letter-spacing: 0.07rem; + font-weight: normal; + font-weight: 600; + text-align: right; +} +.headerMenu { + width: 100%; +} +.headerMenu ul { + display: flex; + flex-wrap: wrap; + padding: 0 2rem; +} +.headerMenu ul li { + font-size: 1rem; + line-height: 2rem; + letter-spacing: 0.07rem; + font-weight: normal; + border-left: 1px #e6e6e6 solid; +} +.headerMenu ul li a { + display: block; + padding: 0 2rem; + color: #333333; + text-decoration: none; + -moz-transition: all 0.3s ease-out; + -ms-transition: all 0.3s ease-out; + -webkit-transition: all 0.3s ease-out; + transition: all 0.3s ease-out; +} +.headerMenu ul li a:hover { + background: #fafafa; + text-decoration: underline; +} +.headerMenu ul li a.isActive { + font-weight: 600; + pointer-events: none; + position: relative; +} +.headerMenu ul li a.isActive::after { + content: ""; + border-right: 0.6rem transparent solid; + border-bottom: 0.6rem #282828 solid; + border-left: 0.6rem transparent solid; + position: absolute; + bottom: -4px; + left: 50%; + transform: translateX(-50%); +} + +.accountInfo { + position: absolute; + right: 2rem; + bottom: 0.3rem; + font-size: 0.8rem; + line-height: 1.6rem; + letter-spacing: 0.04rem; + font-weight: normal; +} +.accountIcon { + width: 1.4rem; + vertical-align: top; +} diff --git a/dictation_client/src/components/header/loginedHeader.module.scss.d.ts b/dictation_client/src/components/header/loginedHeader.module.scss.d.ts new file mode 100644 index 0000000..2e3ed1a --- /dev/null +++ b/dictation_client/src/components/header/loginedHeader.module.scss.d.ts @@ -0,0 +1,10 @@ +declare const classNames: { + readonly header: "header"; + readonly headerLogo: "headerLogo"; + readonly headerSub: "headerSub"; + readonly headerMenu: "headerMenu"; + readonly isActive: "isActive"; + readonly accountInfo: "accountInfo"; + readonly accountIcon: "accountIcon"; +}; +export = classNames; diff --git a/dictation_client/src/components/header/loginedHeader.tsx b/dictation_client/src/components/header/loginedHeader.tsx new file mode 100644 index 0000000..25f6c82 --- /dev/null +++ b/dictation_client/src/components/header/loginedHeader.tsx @@ -0,0 +1,51 @@ +import React from "react"; +import styles from "./loginedHeader.module.scss"; + +import logo from "../../assets/images/OMS_logo_black.svg"; +import ac from "../../assets/images/account_circle.svg"; +import { LoginedPaths } from "./types"; +import { HEADER_MENUS, HEADER_NAME } from "./constants"; + +interface HeaderProps { + // userRole: "user" | "partner"; ログインユーザーのロールに応じてタブの活性非活性に使用する想定 + name: string; + activePath: LoginedPaths; +} + +// ログイン後のヘッダー +const LoginedHeader: React.FC = (props: HeaderProps) => { + const { name, activePath } = props; + + return ( +
+
+ OM System +
+
{HEADER_NAME}
+
+ +

+ + + {name} +

+
+
+ ); +}; +export default LoginedHeader; diff --git a/dictation_client/src/components/header/notLoginHeader.tsx b/dictation_client/src/components/header/notLoginHeader.tsx new file mode 100644 index 0000000..ba5b532 --- /dev/null +++ b/dictation_client/src/components/header/notLoginHeader.tsx @@ -0,0 +1,27 @@ +import React from "react"; +import styles from "./header.module.scss"; + +import logo from "../../assets/images/OMS_logo_black.svg"; +import { HEADER_NAME } from "./constants"; + +interface NotLoginHeaderProps { + isMobile?: boolean; +} +// ログインしていない時のヘッダー +const NotLoginHeader: React.FC = ( + props: NotLoginHeaderProps +) => { + const { isMobile } = props; + return ( +
+
+ OM System +
+

{HEADER_NAME}

+
+ ); +}; +NotLoginHeader.defaultProps = { + isMobile: false, +}; +export default NotLoginHeader; diff --git a/dictation_client/src/components/header/types.ts b/dictation_client/src/components/header/types.ts new file mode 100644 index 0000000..b5a2d62 --- /dev/null +++ b/dictation_client/src/components/header/types.ts @@ -0,0 +1,20 @@ +// ページパス +export type Paths = LoginedPaths | "/" | "/signup" | "login"; + +// ログイン後のヘッダータブ +export type HeaderMenus = + | "Account" + | "User" + | "License" + | "Dictations" + | "Workflow" + | "XXX"; + +// ログイン後に遷移しうるパス +export type LoginedPaths = + | "/account" + | "/user" + | "/license" + | "/dictations" + | "/workflow" + | "/xxx"; diff --git a/dictation_client/src/components/header/utils.ts b/dictation_client/src/components/header/utils.ts new file mode 100644 index 0000000..3c0a721 --- /dev/null +++ b/dictation_client/src/components/header/utils.ts @@ -0,0 +1,21 @@ +import { LoginedPaths } from "./types"; + +// ログイン後のパスかどうか判定 +export const isLoginPaths = (d: string): d is LoginedPaths => { + // caseに入力補完で取りうるリテラルしか出なくする + const type = d as LoginedPaths; + switch (type) { + case "/account": + case "/user": + case "/license": + case "/dictations": + case "/workflow": + case "/xxx": + return true; + default: { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const _: never = type; + return false; + } + } +}; diff --git a/dictation_client/src/pages/ErrorPage/index.tsx b/dictation_client/src/pages/ErrorPage/index.tsx index e8fb978..4592ea0 100644 --- a/dictation_client/src/pages/ErrorPage/index.tsx +++ b/dictation_client/src/pages/ErrorPage/index.tsx @@ -1,9 +1,7 @@ -import { UpdateTokenTimer } from "components/auth/updateTokenTimer"; import React from "react"; export const AuthErrorPage = (): JSX.Element => (
-

ログインに失敗しました


diff --git a/dictation_client/src/pages/LoginPage/index.tsx b/dictation_client/src/pages/LoginPage/index.tsx index 32ac481..8691e3f 100644 --- a/dictation_client/src/pages/LoginPage/index.tsx +++ b/dictation_client/src/pages/LoginPage/index.tsx @@ -1,6 +1,8 @@ import { InteractionStatus, SilentRequest } from "@azure/msal-browser"; import { useIsAuthenticated, useMsal } from "@azure/msal-react"; import { AppDispatch } from "app/store"; +import Footer from "components/footer"; +import Header from "components/header"; import { loginAsync } from "features/login"; import React, { useCallback, useEffect } from "react"; import { useDispatch } from "react-redux"; @@ -26,7 +28,7 @@ const LoginPage: React.FC = (): JSX.Element => { }); } if (meta.requestStatus === "fulfilled") { - navigate("/XXX"); + navigate("/xxx"); } }, [accounts, dispatch, instance, navigate]); @@ -45,7 +47,13 @@ const LoginPage: React.FC = (): JSX.Element => { navigate, ]); - return

loading ...

; + return ( + <> +
+

loading ...

+