Compare commits
8 Commits
1938e95948
...
030fc1a808
Author | SHA1 | Date | |
---|---|---|---|
![]() |
030fc1a808 | ||
![]() |
05c69481b5 | ||
![]() |
0fcc194860 | ||
![]() |
56f0f30ffb | ||
![]() |
0aefbfaeae | ||
![]() |
628c72190b | ||
![]() |
886a314df7 | ||
![]() |
efc2d31f7c |
@@ -2,7 +2,7 @@
|
|||||||
tags: cms, login-flow
|
tags: cms, login-flow
|
||||||
---
|
---
|
||||||
|
|
||||||
# login flow
|
# CMS login flow
|
||||||
|
|
||||||
## description
|
## description
|
||||||
|
|
||||||
|
37
001_documentation/Requirements/REQ0020/index.md
Normal file
37
001_documentation/Requirements/REQ0020/index.md
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
tags: mobile, login-flow
|
||||||
|
---
|
||||||
|
|
||||||
|
# Mobile login flow
|
||||||
|
|
||||||
|
## description
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TD;
|
||||||
|
Start-->A;
|
||||||
|
A-->B;
|
||||||
|
B-->C;
|
||||||
|
B-->D;
|
||||||
|
D-->E;
|
||||||
|
E-->F;
|
||||||
|
C-->G;
|
||||||
|
G-->A
|
||||||
|
|
||||||
|
F-->End;
|
||||||
|
|
||||||
|
A[greeting, asking username and password]
|
||||||
|
B[check if username and password is valid]
|
||||||
|
C[pasword failed]
|
||||||
|
D[pasword ok]
|
||||||
|
E[login success]
|
||||||
|
F[redirect to '/dashboard']
|
||||||
|
|
||||||
|
G[prompt user wrong username and password]
|
||||||
|
|
||||||
|
Start((start));
|
||||||
|
End((end))
|
||||||
|
```
|
||||||
|
|
||||||
|
### relations
|
||||||
|
|
||||||
|
[REQ0016](../REQ0016/index.md)
|
@@ -17,7 +17,8 @@
|
|||||||
- [REQ0013: cms dashboard](./REQ0013/index.md)
|
- [REQ0013: cms dashboard](./REQ0013/index.md)
|
||||||
- [REQ0014: mobile client](./REQ0014/index.md)
|
- [REQ0014: mobile client](./REQ0014/index.md)
|
||||||
- [REQ0015: pocketbase json schema to dbml converter](./REQ0015/index.md)
|
- [REQ0015: pocketbase json schema to dbml converter](./REQ0015/index.md)
|
||||||
- [REQ0016: login flow](./REQ0016/index.md)
|
- [REQ0016: CMS login flow](./REQ0016/index.md)
|
||||||
- [REQ0017: lesson page documentation](./REQ0017/index.md)
|
- [REQ0017: lesson page documentation](./REQ0017/index.md)
|
||||||
- [REQ0018: family photo of frameworks](./REQ0018/index.md)
|
- [REQ0018: family photo of frameworks](./REQ0018/index.md)
|
||||||
- [REQ0019: System architecture](./REQ0019/index.md)
|
- [REQ0019: System architecture](./REQ0019/index.md)
|
||||||
|
- [REQ0020: Mobile login flow](./REQ0020/index.md)
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
# TODO
|
# TODO
|
||||||
|
|
||||||
- [ ] add login mechanism
|
- [ ] add login mechanism
|
||||||
|
- [ ] add task server handle callback tasks
|
||||||
|
@@ -1,9 +1,14 @@
|
|||||||
const Paths = {
|
const Paths = {
|
||||||
AuthHome: `/auth/Home`,
|
AuthHome: `/auth/home`,
|
||||||
AuthLogin: `/auth/login`,
|
AuthLogin: `/auth/login`,
|
||||||
AuthSignUp: `/auth/signup`,
|
AuthSignUp: `/auth/signup`,
|
||||||
SignUpSuccess: `/auth/sign_up_success`,
|
SignUpSuccess: `/auth/sign_up_success`,
|
||||||
AuthorizedTest: `/auth/authorized_test`,
|
AuthorizedTest: `/auth/authorized_test`,
|
||||||
|
//
|
||||||
|
StudentInfo: `/auth/student_info/:id`,
|
||||||
|
GetStudentInfoLink: (id: string) => `/auth/student_info/${id}`,
|
||||||
|
//
|
||||||
|
Setting: `/setting`,
|
||||||
};
|
};
|
||||||
|
|
||||||
export { Paths };
|
export { Paths };
|
||||||
|
@@ -45,14 +45,16 @@ import Page from './pages/Page';
|
|||||||
import QuizzesMainMenu from './pages/QuizzesMainMenu';
|
import QuizzesMainMenu from './pages/QuizzesMainMenu';
|
||||||
//
|
//
|
||||||
import MyAchievementPage from './pages/Record/index';
|
import MyAchievementPage from './pages/Record/index';
|
||||||
import Setting from './pages/Setting/indx';
|
import Setting from './pages/Setting';
|
||||||
import Tab1 from './pages/Tab1';
|
import Tab1 from './pages/Tab1';
|
||||||
import Tab2 from './pages/Tab2';
|
import Tab2 from './pages/Tab2';
|
||||||
import Tab3 from './pages/Tab3';
|
import Tab3 from './pages/Tab3';
|
||||||
import { Paths } from './Paths';
|
import { Paths } from './Paths';
|
||||||
import SignUpSuccess from './pages/auth/SignUpSuccess';
|
import SignUpSuccess from './pages/auth/SignUpSuccess';
|
||||||
import AuthorizedTest from './pages/auth/AuthorizedTest';
|
import AuthorizedTest from './pages/auth/AuthorizedTest';
|
||||||
import { AuthGuard } from './pages/auth/AuthorizedTest/auth-guard';
|
import { AuthGuard } from './components/auth/auth-guard';
|
||||||
|
import StudentInfo from './pages/auth/StudentInfo';
|
||||||
|
// import { AuthGuard } from './pages/auth/AuthorizedTest/auth-guard';
|
||||||
// import WordPageWithLayout from './pages/Lesson/WordPageWithLayout.del';
|
// import WordPageWithLayout from './pages/Lesson/WordPageWithLayout.del';
|
||||||
|
|
||||||
function RouteConfig() {
|
function RouteConfig() {
|
||||||
@@ -180,11 +182,15 @@ function RouteConfig() {
|
|||||||
<SignUpSuccess />
|
<SignUpSuccess />
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
<Route exact path={Paths.AuthorizedTest}>
|
{/* protected page */}
|
||||||
<AuthGuard>
|
<AuthGuard>
|
||||||
|
<Route exact path={Paths.AuthorizedTest}>
|
||||||
<AuthorizedTest />
|
<AuthorizedTest />
|
||||||
</AuthGuard>
|
|
||||||
</Route>
|
</Route>
|
||||||
|
<Route exact path={Paths.StudentInfo}>
|
||||||
|
<StudentInfo />
|
||||||
|
</Route>
|
||||||
|
</AuthGuard>
|
||||||
|
|
||||||
{/* TODO: remove below */}
|
{/* TODO: remove below */}
|
||||||
<Route exact path="/tab1">
|
<Route exact path="/tab1">
|
||||||
@@ -196,7 +202,7 @@ function RouteConfig() {
|
|||||||
<Route path="/tab3">
|
<Route path="/tab3">
|
||||||
<Tab3 />
|
<Tab3 />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/setting">
|
<Route path={Paths.Setting}>
|
||||||
<Setting />
|
<Setting />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/page/:name" exact={true}>
|
<Route path="/page/:name" exact={true}>
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
import { useIonRouter } from '@ionic/react';
|
import { useIonRouter } from '@ionic/react';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { IonAlert, IonButton } from '@ionic/react';
|
import { IonAlert, IonButton } from '@ionic/react';
|
||||||
import { useUser } from '../../../hooks/use-user';
|
import { useUser } from '../../hooks/use-user';
|
||||||
import { Paths } from '../../../Paths';
|
import { Paths } from '../../Paths';
|
||||||
|
|
||||||
export interface AuthGuardProps {
|
export interface AuthGuardProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
@@ -4,5 +4,8 @@ import { COL_USER_METAS } from '../../constants';
|
|||||||
import { pb } from '../../lib/pb';
|
import { pb } from '../../lib/pb';
|
||||||
|
|
||||||
export async function getUserMetaById(id: string): Promise<RecordModel> {
|
export async function getUserMetaById(id: string): Promise<RecordModel> {
|
||||||
return pb.collection(COL_USER_METAS).getOne(id);
|
return pb.collection(COL_USER_METAS).getOne(id, {
|
||||||
|
expand: 'billingAddress',
|
||||||
|
requestKey: null,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,19 @@
|
|||||||
import type { BillingAddress } from '@/components/dashboard/user_meta/type.d';
|
import type { BillingAddress } from '@/components/dashboard/user_meta/type.d';
|
||||||
|
|
||||||
|
// DBUserMeta type definitions
|
||||||
|
export interface DBUserMeta {
|
||||||
|
name: string;
|
||||||
|
avatar: string;
|
||||||
|
email: string;
|
||||||
|
phone: string;
|
||||||
|
quota: number;
|
||||||
|
status: 'active' | 'blocked' | 'pending';
|
||||||
|
//
|
||||||
|
collectionId: string;
|
||||||
|
id: string;
|
||||||
|
createdAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
// UserMeta type definitions
|
// UserMeta type definitions
|
||||||
export interface UserMeta {
|
export interface UserMeta {
|
||||||
id: string;
|
id: string;
|
||||||
|
5
002_source/ionic_mobile/src/lib/getStudentAvatar.tsx
Normal file
5
002_source/ionic_mobile/src/lib/getStudentAvatar.tsx
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { DBUserMeta } from '../db/UserMetas/type';
|
||||||
|
|
||||||
|
export function getStudentAvatar(studentMeta: DBUserMeta) {
|
||||||
|
return `url(http://localhost:8090/api/files/${studentMeta.collectionId}/${studentMeta.id}/${studentMeta.avatar})`;
|
||||||
|
}
|
@@ -35,7 +35,7 @@ const LessonContainer: React.FC<ContainerProps> = ({ lesson_type_id: lesson_type
|
|||||||
|
|
||||||
if (loading) return <LoadingScreen />;
|
if (loading) return <LoadingScreen />;
|
||||||
if (!selected_content) return <LoadingScreen />;
|
if (!selected_content) return <LoadingScreen />;
|
||||||
if (selected_content.length == 0) return <>loading</>;
|
if (selected_content.length == 0) return <LoadingScreen />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@@ -22,12 +22,25 @@ import _ from 'lodash';
|
|||||||
import { Router, useParams } from 'react-router';
|
import { Router, useParams } from 'react-router';
|
||||||
import { Wave } from '../../../components/Wave';
|
import { Wave } from '../../../components/Wave';
|
||||||
import { Paths } from '../../../Paths';
|
import { Paths } from '../../../Paths';
|
||||||
|
import { useTransition } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useUser } from '../../../hooks/use-user';
|
||||||
|
|
||||||
function AuthorizedTest(): React.JSX.Element {
|
function AuthorizedTest(): React.JSX.Element {
|
||||||
const router = useIonRouter();
|
const router = useIonRouter();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { user } = useUser();
|
||||||
|
|
||||||
function handleBackToLogin() {
|
function handleBackToLogin() {
|
||||||
router.push(Paths.AuthLogin);
|
router.push(Paths.AuthLogin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleViewStudentInfoOnClick() {
|
||||||
|
if (user?.id) {
|
||||||
|
router.push(Paths.GetStudentInfoLink(user.id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IonPage className={styles.loginPage}>
|
<IonPage className={styles.loginPage}>
|
||||||
<IonHeader>{/* */}</IonHeader>
|
<IonHeader>{/* */}</IonHeader>
|
||||||
@@ -36,6 +49,12 @@ function AuthorizedTest(): React.JSX.Element {
|
|||||||
<IonGrid className="ion-padding">
|
<IonGrid className="ion-padding">
|
||||||
<IonCol>
|
<IonCol>
|
||||||
<IonRow>Authorized page test</IonRow>
|
<IonRow>Authorized page test</IonRow>
|
||||||
|
{JSON.stringify({ user })}
|
||||||
|
{/* */}
|
||||||
|
<IonRow>
|
||||||
|
<IonButton onClick={handleViewStudentInfoOnClick}>{t('view-student-info')}</IonButton>
|
||||||
|
</IonRow>
|
||||||
|
{/* */}
|
||||||
<IonRow>
|
<IonRow>
|
||||||
<IonButton onClick={handleBackToLogin}>Back to login</IonButton>
|
<IonButton onClick={handleBackToLogin}>Back to login</IonButton>
|
||||||
</IonRow>
|
</IonRow>
|
||||||
|
107
002_source/ionic_mobile/src/pages/auth/Home/index copy.tsx
Normal file
107
002_source/ionic_mobile/src/pages/auth/Home/index copy.tsx
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
import {
|
||||||
|
IonButton,
|
||||||
|
IonCardTitle,
|
||||||
|
IonCol,
|
||||||
|
IonContent,
|
||||||
|
IonFooter,
|
||||||
|
IonGrid,
|
||||||
|
IonHeader,
|
||||||
|
IonImg,
|
||||||
|
IonPage,
|
||||||
|
IonRouterLink,
|
||||||
|
IonRow,
|
||||||
|
IonToolbar,
|
||||||
|
useIonRouter,
|
||||||
|
} from '@ionic/react';
|
||||||
|
// import { Action } from '../components/Action';
|
||||||
|
import styles from './style.module.scss';
|
||||||
|
import { Action } from '../../../components/Action';
|
||||||
|
import { Paths } from '../../../Paths';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { useUser } from '../../../hooks/use-user';
|
||||||
|
import { LoadingScreen } from '../../../components/LoadingScreen';
|
||||||
|
|
||||||
|
const AuthHome = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { user, checkSession, isLoading } = useUser();
|
||||||
|
const router = useIonRouter();
|
||||||
|
|
||||||
|
const [showLoading, setShowLoading] = useState<boolean>(true);
|
||||||
|
const [showError, setShowErrr] = useState<{ show: boolean; message: string }>({
|
||||||
|
show: false,
|
||||||
|
message: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const [checkingSession, setCheckingSession] = useState<boolean>(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!checkingSession) {
|
||||||
|
if (!user) {
|
||||||
|
router.push(Paths.AuthLogin);
|
||||||
|
} else {
|
||||||
|
router.push(Paths.AuthorizedTest);
|
||||||
|
}
|
||||||
|
setShowLoading(false);
|
||||||
|
}
|
||||||
|
}, [user, checkingSession]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
checkSession?.()
|
||||||
|
.then(() => {
|
||||||
|
setCheckingSession(false);
|
||||||
|
})
|
||||||
|
.catch((err) => console.error(err));
|
||||||
|
}, [checkSession]);
|
||||||
|
|
||||||
|
if (showLoading) return <LoadingScreen />;
|
||||||
|
// if (showError) return <>{showError.message}</>;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IonPage className={'styles.homePage'}>
|
||||||
|
<IonHeader>
|
||||||
|
{/* <IonToolbar className="ion-no-margin ion-no-padding"> */}
|
||||||
|
<IonImg src="/assets/login2.jpeg" />
|
||||||
|
{/* </IonToolbar> */}
|
||||||
|
</IonHeader>
|
||||||
|
<IonContent fullscreen>
|
||||||
|
<div className={styles.getStarted}>
|
||||||
|
<IonGrid>
|
||||||
|
<IonRow className={`ion-text-center ion-justify-content-center ${styles.heading}`}>
|
||||||
|
<IonCol size="11" className={styles.headingText}>
|
||||||
|
<IonCardTitle>
|
||||||
|
{/* */}
|
||||||
|
Join millions of other people discovering their creative side
|
||||||
|
</IonCardTitle>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
|
||||||
|
<IonRow className={`ion-text-center ion-justify-content-center`}>
|
||||||
|
<IonRouterLink routerLink={Paths.AuthSignUp} className="custom-link">
|
||||||
|
<IonCol size="11">
|
||||||
|
<IonButton className={`${styles.getStartedButton} custom-button`}>
|
||||||
|
{/* */}
|
||||||
|
Get started →
|
||||||
|
</IonButton>
|
||||||
|
</IonCol>
|
||||||
|
</IonRouterLink>
|
||||||
|
</IonRow>
|
||||||
|
</IonGrid>
|
||||||
|
</div>
|
||||||
|
</IonContent>
|
||||||
|
|
||||||
|
<IonFooter>
|
||||||
|
<IonGrid style={{ marginBottom: '1rem' }}>
|
||||||
|
<Action
|
||||||
|
message={t('already-got-an-account')}
|
||||||
|
text={t('login')}
|
||||||
|
link={Paths.AuthLogin}
|
||||||
|
//
|
||||||
|
/>
|
||||||
|
</IonGrid>
|
||||||
|
</IonFooter>
|
||||||
|
</IonPage>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AuthHome;
|
@@ -11,16 +11,33 @@ import {
|
|||||||
IonRouterLink,
|
IonRouterLink,
|
||||||
IonRow,
|
IonRow,
|
||||||
IonToolbar,
|
IonToolbar,
|
||||||
|
useIonRouter,
|
||||||
} from '@ionic/react';
|
} from '@ionic/react';
|
||||||
// import { Action } from '../components/Action';
|
// import { Action } from '../components/Action';
|
||||||
import styles from './style.module.scss';
|
import styles from './style.module.scss';
|
||||||
import { Action } from '../../../components/Action';
|
import { Action } from '../../../components/Action';
|
||||||
import { Paths } from '../../../Paths';
|
import { Paths } from '../../../Paths';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { useUser } from '../../../hooks/use-user';
|
||||||
|
import { LoadingScreen } from '../../../components/LoadingScreen';
|
||||||
|
|
||||||
const AuthHome = () => {
|
const AuthHome = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const [showLoading, setShowLoading] = useState<boolean>(true);
|
||||||
|
const [showError, setShowErrr] = useState<{ show: boolean; message: string }>({
|
||||||
|
show: false,
|
||||||
|
message: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setShowLoading(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (showLoading) return <LoadingScreen />;
|
||||||
|
if (showError.show) return <>{showError.message}</>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IonPage className={'styles.homePage'}>
|
<IonPage className={'styles.homePage'}>
|
||||||
<IonHeader>
|
<IonHeader>
|
||||||
|
@@ -38,6 +38,7 @@ import { z as zod } from 'zod';
|
|||||||
import { pb } from '../../../lib/pb';
|
import { pb } from '../../../lib/pb';
|
||||||
import { ClientResponseError } from 'pocketbase';
|
import { ClientResponseError } from 'pocketbase';
|
||||||
import { COL_USER_METAS, COL_USERS } from '../../../constants';
|
import { COL_USER_METAS, COL_USERS } from '../../../constants';
|
||||||
|
import { Paths } from '../../../Paths';
|
||||||
|
|
||||||
function AuthSignUp(): React.JSX.Element {
|
function AuthSignUp(): React.JSX.Element {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
@@ -109,6 +110,8 @@ function AuthSignUp(): React.JSX.Element {
|
|||||||
};
|
};
|
||||||
const userMetaRecord = await pb.collection(COL_USER_METAS).create(userMeta);
|
const userMetaRecord = await pb.collection(COL_USER_METAS).create(userMeta);
|
||||||
await pb.collection('users').requestVerification(user.email);
|
await pb.collection('users').requestVerification(user.email);
|
||||||
|
|
||||||
|
router.push(Paths.SignUpSuccess);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
const res_err = err as unknown as ClientResponseError;
|
const res_err = err as unknown as ClientResponseError;
|
||||||
const {
|
const {
|
||||||
@@ -133,19 +136,7 @@ function AuthSignUp(): React.JSX.Element {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<IonPage className={styles.loginPage}>
|
<IonPage className={styles.loginPage}>
|
||||||
<IonHeader>
|
<IonHeader></IonHeader>
|
||||||
<IonToolbar>
|
|
||||||
<IonButtons slot="start">
|
|
||||||
<IonBackButton icon={arrowBack} text="" className="custom-back" />
|
|
||||||
</IonButtons>
|
|
||||||
|
|
||||||
<IonButtons slot="end">
|
|
||||||
<IonButton className="custom-button">
|
|
||||||
<IonIcon icon={shapesOutline} />
|
|
||||||
</IonButton>
|
|
||||||
</IonButtons>
|
|
||||||
</IonToolbar>
|
|
||||||
</IonHeader>
|
|
||||||
{/* */}
|
{/* */}
|
||||||
<IonContent fullscreen>
|
<IonContent fullscreen>
|
||||||
<form onSubmit={handleSubmit(onSubmit)}>
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
|
154
002_source/ionic_mobile/src/pages/auth/StudentInfo/index.tsx
Normal file
154
002_source/ionic_mobile/src/pages/auth/StudentInfo/index.tsx
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
import {
|
||||||
|
IonButton,
|
||||||
|
IonCol,
|
||||||
|
IonContent,
|
||||||
|
IonFooter,
|
||||||
|
IonGrid,
|
||||||
|
IonHeader,
|
||||||
|
IonPage,
|
||||||
|
IonRow,
|
||||||
|
IonText,
|
||||||
|
useIonRouter,
|
||||||
|
} from '@ionic/react';
|
||||||
|
import styles from './style.module.scss';
|
||||||
|
import { useParams } from 'react-router';
|
||||||
|
import { Wave } from '../../../components/Wave';
|
||||||
|
import { Paths } from '../../../Paths';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { getUserMetaById } from '../../../db/UserMetas/GetById';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { DBUserMeta } from '../../../db/UserMetas/type';
|
||||||
|
import { LoadingScreen } from '../../../components/LoadingScreen';
|
||||||
|
import { getStudentAvatar } from '../../../lib/getStudentAvatar';
|
||||||
|
import { authClient } from '../../../lib/auth/custom/client';
|
||||||
|
import { useUser } from '../../../hooks/use-user';
|
||||||
|
|
||||||
|
function StudentInfo(): React.JSX.Element {
|
||||||
|
const router = useIonRouter();
|
||||||
|
const { id } = useParams<{ id: string }>();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const [studentMeta, setStudentMeta] = useState<DBUserMeta>();
|
||||||
|
const test = useUser();
|
||||||
|
|
||||||
|
const [showLoading, setShowLoading] = useState<boolean>(true);
|
||||||
|
const [showError, setShowError] = useState<{ show: boolean; message: string }>({ show: false, message: '' });
|
||||||
|
|
||||||
|
function handleBackToLogin() {
|
||||||
|
router.push(Paths.AuthLogin);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleBackOnClick() {
|
||||||
|
router.push(Paths.Setting);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleFetchUserMeta() {
|
||||||
|
try {
|
||||||
|
const result = await getUserMetaById(id);
|
||||||
|
const tempStudentMeta = result as unknown as DBUserMeta;
|
||||||
|
|
||||||
|
setStudentMeta(tempStudentMeta);
|
||||||
|
setShowLoading(false);
|
||||||
|
} catch (error) {
|
||||||
|
setShowError({ show: true, message: JSON.stringify({ error }, null, 2) });
|
||||||
|
setShowLoading(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleLogoutOnClick() {
|
||||||
|
try {
|
||||||
|
await authClient.signOut();
|
||||||
|
router.push(Paths.AuthLogin);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
void handleFetchUserMeta();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (showLoading) return <LoadingScreen />;
|
||||||
|
if (!studentMeta) return <LoadingScreen />;
|
||||||
|
if (showError.show) return <>{showError.message}</>;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IonPage className={styles.loginPage}>
|
||||||
|
<IonHeader>{/* */}</IonHeader>
|
||||||
|
{/* */}
|
||||||
|
<IonContent fullscreen>
|
||||||
|
<IonGrid className="ion-padding">
|
||||||
|
<IonRow className="ion-justify-content-center">
|
||||||
|
<IonCol size={'3'}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
backgroundImage: getStudentAvatar(studentMeta),
|
||||||
|
backgroundSize: 'cover',
|
||||||
|
backgroundPosition: 'center',
|
||||||
|
backgroundRepeat: 'no-repeat',
|
||||||
|
//
|
||||||
|
width: '25vw',
|
||||||
|
height: '25vw',
|
||||||
|
//
|
||||||
|
borderRadius: 'calc( 25vw / 2 )',
|
||||||
|
}}
|
||||||
|
></div>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
{/* */}
|
||||||
|
<IonRow
|
||||||
|
className="ion-justify-content-between"
|
||||||
|
style={{
|
||||||
|
marginTop: '1rem',
|
||||||
|
marginBottom: '1rem',
|
||||||
|
//
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IonText>{t('student-name')}</IonText>
|
||||||
|
<IonText>{studentMeta.name}</IonText>
|
||||||
|
</IonRow>
|
||||||
|
{/* */}
|
||||||
|
<IonRow
|
||||||
|
className="ion-justify-content-between"
|
||||||
|
style={{
|
||||||
|
marginTop: '1rem',
|
||||||
|
marginBottom: '1rem',
|
||||||
|
//
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IonText>{t('student-email')}</IonText>
|
||||||
|
<IonText>{studentMeta.email}</IonText>
|
||||||
|
</IonRow>
|
||||||
|
{/* */}
|
||||||
|
<IonRow
|
||||||
|
className="ion-justify-content-between"
|
||||||
|
style={{
|
||||||
|
marginTop: '1rem',
|
||||||
|
marginBottom: '1rem',
|
||||||
|
//
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IonText>{t('student-phone')}</IonText>
|
||||||
|
<IonText>{studentMeta.phone}</IonText>
|
||||||
|
</IonRow>
|
||||||
|
{/* */}
|
||||||
|
<IonRow className="ion-justify-content-center">
|
||||||
|
<IonButton onClick={handleBackOnClick}>{t('back')}</IonButton>
|
||||||
|
</IonRow>
|
||||||
|
|
||||||
|
<IonRow className="ion-justify-content-center">
|
||||||
|
<IonButton onClick={handleLogoutOnClick}>{t('logout')}</IonButton>
|
||||||
|
</IonRow>
|
||||||
|
</IonGrid>
|
||||||
|
</IonContent>
|
||||||
|
{/* */}
|
||||||
|
<IonFooter>
|
||||||
|
<IonGrid className="ion-no-margin ion-no-padding">
|
||||||
|
<Wave />
|
||||||
|
</IonGrid>
|
||||||
|
</IonFooter>
|
||||||
|
</IonPage>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default StudentInfo;
|
@@ -0,0 +1,17 @@
|
|||||||
|
.signupPage {
|
||||||
|
ion-toolbar {
|
||||||
|
--border-style: none;
|
||||||
|
--border-color: transparent;
|
||||||
|
--padding-top: 1rem;
|
||||||
|
--padding-bottom: 1rem;
|
||||||
|
--padding-start: 1rem;
|
||||||
|
--padding-end: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.headingText {
|
||||||
|
h5 {
|
||||||
|
margin-top: 0.2rem;
|
||||||
|
// color: #d3a6c7;
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user