Add fake login page until CAS access is granted #11

Manually merged
kadet merged 1 commits from feature_validation into master 2020-08-18 20:59:06 +02:00
8 changed files with 95 additions and 39 deletions

View File

@ -4,7 +4,6 @@ import { route, routes } from "@/routing";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import { AppState, isReady } from "@/state/reducer"; import { AppState, isReady } from "@/state/reducer";
import { StudentActions } from "@/state/actions/student"; import { StudentActions } from "@/state/actions/student";
import { sampleStudent } from "@/provider/dummy/student";
import { Trans, useTranslation } from "react-i18next"; import { Trans, useTranslation } from "react-i18next";
import { Student } from "@/data"; import { Student } from "@/data";
import '@/styles/overrides.scss' import '@/styles/overrides.scss'
@ -26,13 +25,6 @@ const UserMenu = (props: HTMLProps<HTMLUListElement>) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const { t } = useTranslation(); const { t } = useTranslation();
const handleUserLogin = () => {
dispatch({
type: StudentActions.Login,
student: sampleStudent,
})
}
const handleUserLogout = () => { const handleUserLogout = () => {
dispatch({ type: StudentActions.Logout }) dispatch({ type: StudentActions.Logout })
} }
@ -44,7 +36,7 @@ const UserMenu = (props: HTMLProps<HTMLUListElement>) => {
{ ' ' } { ' ' }
(<Link to={ '#' } onClick={ handleUserLogout }>{ t('logout') }</Link>) (<Link to={ '#' } onClick={ handleUserLogout }>{ t('logout') }</Link>)
</> : <> </> : <>
<Link to={ '#' } onClick={ handleUserLogin }>{ t('login') }</Link> <Link to={ route('user_login') }>{ t('login') }</Link>
</> </>
} }
</ul>; </ul>;

View File

@ -284,7 +284,7 @@ export const InternshipForm: React.FunctionComponent = () => {
postalCode: Yup.string().required(t("validation.required")), postalCode: Yup.string().required(t("validation.required")),
building: Yup.string().required(t("validation.required")), building: Yup.string().required(t("validation.required")),
kindOther: Yup.string().when("kind", { kindOther: Yup.string().when("kind", {
is: (values: InternshipFormValues) => values?.kind !== InternshipType.Other, is: (values: InternshipFormValues) => values?.kind === InternshipType.Other,
then: Yup.string().required(t("validation.required")) then: Yup.string().required(t("validation.required"))
}) })
}) })
@ -312,6 +312,8 @@ export const InternshipForm: React.FunctionComponent = () => {
if (Object.keys(errors).length == 0) { if (Object.keys(errors).length == 0) {
setConfirmDialogOpen(true); setConfirmDialogOpen(true);
} else {
console.warn(errors);
} }
} }

View File

@ -1,7 +1,7 @@
import React, { useMemo } from "react"; import React, { useMemo } from "react";
import { Page } from "@/pages/base"; import { Page } from "@/pages/base";
import { Button, Container, Stepper, Typography } from "@material-ui/core"; import { Button, Container, Stepper, Typography } from "@material-ui/core";
import { Link as RouterLink } from "react-router-dom"; import { Link as RouterLink, Redirect } from "react-router-dom";
import { route } from "@/routing"; import { route } from "@/routing";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
@ -11,40 +11,53 @@ import { Deadlines, Edition, getEditionDeadlines } from "@/data/edition";
import { Step } from "@/components"; import { Step } from "@/components";
import { ProposalStep } from "@/pages/steps/proposal"; import { ProposalStep } from "@/pages/steps/proposal";
import { PlanStep } from "@/pages/steps/plan"; import { PlanStep } from "@/pages/steps/plan";
import { InsuranceStep } from "@/pages/steps/insurance";
import { InsuranceState } from "@/state/reducer/insurance"; import { InsuranceState } from "@/state/reducer/insurance";
import { InsuranceStep } from "@/pages/steps/insurance";
export const MainPage = () => { export const MainPage = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const student = useSelector<AppState, Student | null>(state => state.student); const student = useSelector<AppState, Student | null>(state => state.student);
const deadlines = useSelector<AppState, Deadlines>(state => getEditionDeadlines(state.edition as Edition)); // edition cannot be null at this point const deadlines = useSelector<AppState, Deadlines>(state => getEditionDeadlines(state.edition as Edition)); // edition cannot be null at this point
const insurance = useSelector<AppState, InsuranceState>(root => root.insurance); const insurance = useSelector<AppState, InsuranceState>(root => root.insurance);
const missingStudentData = useMemo(() => student ? getMissingStudentData(student) : [], [student]); const missingStudentData = useMemo(() => student ? getMissingStudentData(student) : [], [student]);
if (!student) {
return <Redirect to={ route("user_login") }/>;
}
function *getSteps() {
yield <Step label={ t('steps.personal-data.header') } completed={ missingStudentData.length === 0 } until={ deadlines.personalData } key="personal-data">
{ missingStudentData.length > 0 && <>
<p>{ t('steps.personal-data.info') }</p>
<ul>
{ missingStudentData.map(field => <li key={ field }>{ t(`student.${ field }`) }</li>) }
</ul>
<Button to={ route("internship_proposal") } variant="contained" color="primary" component={ RouterLink }>
{ t('steps.personal-data.form') }
</Button>
</> }
</Step>;
yield <ProposalStep key="proposal"/>;
yield <PlanStep key="plan"/>;
if (insurance.required)
yield <InsuranceStep key="insurance"/>;
yield <Step label={ t('steps.report.header') } until={ deadlines.report } key="report"/>
yield <Step label={ t('steps.grade.header') } key="grade"/>
}
return <Page my={ 6 }> return <Page my={ 6 }>
<Container> <Container>
<Typography variant="h2">{ t("pages.my-internship.header") }</Typography> <Typography variant="h2">{ t("pages.my-internship.header") }</Typography>
<Stepper orientation="vertical" nonLinear> <Stepper orientation="vertical" nonLinear>
<Step label={ t('steps.personal-data.header') } completed={ missingStudentData.length === 0 } until={ deadlines.personalData }> { Array.from(getSteps()) }
{ missingStudentData.length > 0 && <>
<p>{ t('steps.personal-data.info') }</p>
<ul>
{ missingStudentData.map(field => <li key={ field }>{ t(`student.${ field }`) }</li>) }
</ul>
<Button to={ route("internship_proposal") } variant="contained" color="primary" component={ RouterLink }>
{ t('steps.personal-data.form') }
</Button>
</> }
</Step>
<ProposalStep />
<PlanStep />
{ insurance.required && <InsuranceStep /> }
<Step label={ t('steps.report.header') } until={ deadlines.report }/>
<Step label={ t('steps.grade.header') }/>
</Stepper> </Stepper>
</Container> </Container>
</Page> </Page>

View File

@ -13,11 +13,6 @@ export const InsuranceStep = () => {
const deadline = useSelector<AppState, Moment | undefined>(state => getEditionDeadlines(state.edition as Edition).insurance); // edition cannot be null at this point const deadline = useSelector<AppState, Moment | undefined>(state => getEditionDeadlines(state.edition as Edition).insurance); // edition cannot be null at this point
const { t } = useTranslation(); const { t } = useTranslation();
// we don't want to show this step unless it's required
if (!insurance.required) {
return null;
}
return <Step label={ t("steps.insurance.header") } until={ deadline } completed={ insurance.signed } active={ !insurance.signed }> return <Step label={ t("steps.insurance.header") } until={ deadline } completed={ insurance.signed } active={ !insurance.signed }>
<p>{ t(`steps.insurance.instructions`) }</p> <p>{ t(`steps.insurance.instructions`) }</p>
<Actions> <Actions>

35
src/pages/user/login.tsx Normal file
View File

@ -0,0 +1,35 @@
import React from "react";
import { Page } from "@/pages/base";
import { Button, Container, Typography } from "@material-ui/core";
import { StudentActions, useDispatch } from "@/state/actions";
import { sampleStudent } from "@/provider/dummy";
import { useHistory } from "react-router-dom";
import { route } from "@/routing";
import { useVerticalSpacing } from "@/styles";
export const UserLoginPage = () => {
const dispatch = useDispatch();
const history = useHistory();
const handleSampleLogin = () => {
dispatch({
type: StudentActions.Login,
student: sampleStudent,
})
history.push(route("home"));
}
const classes = useVerticalSpacing(3);
return <Page>
<Page.Header maxWidth="md">
<Page.Title>Tu miało być przekierowanie do logowania PG...</Page.Title>
</Page.Header>
<Container maxWidth="md" className={ classes.root }>
<Typography variant="h3">... ale wciąż czekamy na dostęp :(</Typography>
<Button fullWidth onClick={ handleSampleLogin } variant="contained" color="primary">Zaloguj jako przykładowy student</Button>
</Container>
</Page>;
}

View File

@ -4,10 +4,12 @@ import { RouteProps } from "react-router-dom";
import { InternshipProposalFormPage, InternshipProposalPreviewPage } from "@/pages/internship/proposal"; import { InternshipProposalFormPage, InternshipProposalPreviewPage } from "@/pages/internship/proposal";
import { NotFoundPage } from "@/pages/errors/not-found"; import { NotFoundPage } from "@/pages/errors/not-found";
import SubmitPlanPage from "@/pages/internship/plan"; import SubmitPlanPage from "@/pages/internship/plan";
import { UserLoginPage } from "@/pages/user/login";
type Route = { type Route = {
name?: string; name?: string;
content: () => ReactComponentElement<any>, content: () => ReactComponentElement<any>,
condition?: () => boolean,
} & RouteProps; } & RouteProps;
export const routes: Route[] = [ export const routes: Route[] = [
@ -17,6 +19,9 @@ export const routes: Route[] = [
{ name: "internship_proposal_preview", path: "/internship/preview/proposal", exact: true, content: () => <InternshipProposalPreviewPage/> }, { name: "internship_proposal_preview", path: "/internship/preview/proposal", exact: true, content: () => <InternshipProposalPreviewPage/> },
{ name: "internship_plan", path: "/internship/plan", exact: true, content: () => <SubmitPlanPage/> }, { name: "internship_plan", path: "/internship/plan", exact: true, content: () => <SubmitPlanPage/> },
// user
{ name: "user_login", path: "/user/login", exact: true, content: () => <UserLoginPage /> },
// fallback route for 404 pages // fallback route for 404 pages
{ name: "fallback", path: "*", content: () => <NotFoundPage/> } { name: "fallback", path: "*", content: () => <NotFoundPage/> }
] ]

View File

@ -1,5 +1,7 @@
import { Reducer } from "react"; import { Reducer } from "react";
import { InsuranceAction, InsuranceActions } from "@/state/actions/insurance"; import { InsuranceAction, InsuranceActions } from "@/state/actions/insurance";
import { InternshipProposalAction, InternshipProposalActions } from "@/state/actions";
import { InternshipType } from "@/data";
export type InsuranceState = { export type InsuranceState = {
required: boolean; required: boolean;
@ -12,10 +14,21 @@ const initialInsuranceState: InsuranceState = {
signed: false, signed: false,
} }
export const insuranceReducer: Reducer<InsuranceState, InsuranceAction> = (state = initialInsuranceState, action) => { export const insuranceReducer: Reducer<InsuranceState, InsuranceAction | InternshipProposalAction> = (state = initialInsuranceState, action) => {
const { type, ...payload } = action; const { type, ...payload } = action;
switch (action.type) { switch (action.type) {
case InternshipProposalActions.Send:
return {
...state,
required: [
InternshipType.FreeApprenticeship,
InternshipType.FreeInternship,
InternshipType.PaidApprenticeship,
InternshipType.GraduateInternship,
].includes(action.internship.type)
}
case InsuranceActions.Signed: case InsuranceActions.Signed:
return { ...state, signed: true } return { ...state, signed: true }
case InsuranceActions.Update: case InsuranceActions.Update:

View File

@ -31,7 +31,7 @@ pages:
my-internship: my-internship:
header: "Moja praktyka" header: "Moja praktyka"
proposal-form: proposal-form:
header: "Propose internship" header: "Zgłoszenie praktyki"
forms: forms:
internship: internship:
@ -138,7 +138,8 @@ steps:
header: "Indywidualny Program Praktyki" header: "Indywidualny Program Praktyki"
info: info:
draft: > draft: >
TODO W porozumieniu z firmą w której odbywają się praktyki należy sporządzić Indywidualny Plan Praktyk zgodnie z
załączonym szablonem a następnie wysłać go do weryfikacji. (TODO)
awaiting: > awaiting: >
Twój indywidualny program praktyki został poprawnie zapisany w systemie. Musi on jeszcze zostać zweryfikowany i Twój indywidualny program praktyki został poprawnie zapisany w systemie. Musi on jeszcze zostać zweryfikowany i
zatwierdzony. Po weryfikacji zostaniesz poinformowany o akceptacji bądź konieczności wprowadzenia zmian. zatwierdzony. Po weryfikacji zostaniesz poinformowany o akceptacji bądź konieczności wprowadzenia zmian.
@ -157,7 +158,7 @@ steps:
insurance: insurance:
header: "Ubezpieczenie NNW" header: "Ubezpieczenie NNW"
instructions: > instructions: >
papierki do podpisania... Należy zgłosić się do pełnomocnika ds. praktyk Twojego kierunku i podpisać umowę ubezpieczenia. (TODO)
validation: validation:
required: "To pole jest wymagane" required: "To pole jest wymagane"