Add more steps
This commit is contained in:
		
							parent
							
								
									16d2ef4315
								
							
						
					
					
						commit
						a3db797a06
					
				| @ -7,6 +7,7 @@ | |||||||
|     "@babel/preset-typescript": "^7.10.1", |     "@babel/preset-typescript": "^7.10.1", | ||||||
|     "@date-io/moment": "^1.3.13", |     "@date-io/moment": "^1.3.13", | ||||||
|     "@material-ui/core": "^4.10.1", |     "@material-ui/core": "^4.10.1", | ||||||
|  |     "@material-ui/icons": "^4.9.1", | ||||||
|     "@material-ui/lab": "^4.0.0-alpha.55", |     "@material-ui/lab": "^4.0.0-alpha.55", | ||||||
|     "@material-ui/pickers": "^3.2.10", |     "@material-ui/pickers": "^3.2.10", | ||||||
|     "@types/classnames": "^2.2.10", |     "@types/classnames": "^2.2.10", | ||||||
|  | |||||||
| @ -12,18 +12,12 @@ import { AppState } from "@/state/reducer"; | |||||||
| import { StudentAction, StudentActions } from "@/state/actions/student"; | import { StudentAction, StudentActions } from "@/state/actions/student"; | ||||||
| import { sampleStudent } from "@/provider/dummy/student"; | import { sampleStudent } from "@/provider/dummy/student"; | ||||||
| import { Trans, useTranslation } from "react-i18next"; | import { Trans, useTranslation } from "react-i18next"; | ||||||
| import { StudentState } from "@/state/reducer/student"; |  | ||||||
| import { Student } from "@/data"; | import { Student } from "@/data"; | ||||||
| 
 |  | ||||||
| import "moment/locale/pl" |  | ||||||
| import '@/styles/overrides.scss' | import '@/styles/overrides.scss' | ||||||
| import '@/styles/header.scss' | import '@/styles/header.scss' | ||||||
| import classNames from "classnames"; | import classNames from "classnames"; | ||||||
| import { Button } from "@material-ui/core"; |  | ||||||
| import { PersistGate } from 'redux-persist/integration/react'; | import { PersistGate } from 'redux-persist/integration/react'; | ||||||
| 
 | 
 | ||||||
| moment.locale("pl") |  | ||||||
| 
 |  | ||||||
| class LocalizedMomentUtils extends MomentUtils { | class LocalizedMomentUtils extends MomentUtils { | ||||||
|     getDatePickerHeaderText(date: Moment): string { |     getDatePickerHeaderText(date: Moment): string { | ||||||
|         return this.format(date, "d MMM yyyy"); |         return this.format(date, "d MMM yyyy"); | ||||||
| @ -65,6 +59,7 @@ const LanguageSwitcher = ({ className, ...props }: HTMLProps<HTMLUListElement>) | |||||||
|     const handleLanguageChange = (language: string) => () => { |     const handleLanguageChange = (language: string) => () => { | ||||||
|         i18n.changeLanguage(language); |         i18n.changeLanguage(language); | ||||||
|         document.documentElement.lang = language; |         document.documentElement.lang = language; | ||||||
|  |         moment.locale(language) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const isActive = (language: string) => language.toLowerCase() === i18n.language.toLowerCase(); |     const isActive = (language: string) => language.toLowerCase() === i18n.language.toLowerCase(); | ||||||
|  | |||||||
| @ -1,6 +1,5 @@ | |||||||
| import { Moment } from "moment"; | import { Moment } from "moment"; | ||||||
| import { Identifiable } from "./common"; | import { Identifiable } from "./common"; | ||||||
| import { countWorkingWeeksInRange } from "@/utils/date"; |  | ||||||
| import { Student } from "@/data/student"; | import { Student } from "@/data/student"; | ||||||
| import { Company } from "@/data/company"; | import { Company } from "@/data/company"; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -2,6 +2,11 @@ import i18n from "i18next"; | |||||||
| import { initReactI18next } from "react-i18next"; | import { initReactI18next } from "react-i18next"; | ||||||
| import I18nextBrowserLanguageDetector from "i18next-browser-languagedetector"; | import I18nextBrowserLanguageDetector from "i18next-browser-languagedetector"; | ||||||
| 
 | 
 | ||||||
|  | import moment from "moment"; | ||||||
|  | 
 | ||||||
|  | import "moment/locale/pl" | ||||||
|  | import "moment/locale/en-gb" | ||||||
|  | 
 | ||||||
| const resources = { | const resources = { | ||||||
|     en: { |     en: { | ||||||
|         translation: require('../translations/en.yaml'), |         translation: require('../translations/en.yaml'), | ||||||
| @ -23,5 +28,6 @@ i18n | |||||||
|     }) |     }) | ||||||
| 
 | 
 | ||||||
| document.documentElement.lang = i18n.language; | document.documentElement.lang = i18n.language; | ||||||
|  | moment.locale(i18n.language) | ||||||
| 
 | 
 | ||||||
| export default i18n; | export default i18n; | ||||||
|  | |||||||
							
								
								
									
										22
									
								
								src/pages/errors/not-found.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/pages/errors/not-found.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | |||||||
|  | import { Page } from "@/pages/base"; | ||||||
|  | import { Box, Button, Container, Divider, Typography } from "@material-ui/core"; | ||||||
|  | import { route } from "@/routing"; | ||||||
|  | import { Link as RouterLink } from "react-router-dom"; | ||||||
|  | import React from "react"; | ||||||
|  | 
 | ||||||
|  | export const NotFoundPage = () => { | ||||||
|  |     return <Page title="Strona nie została znaleziona"> | ||||||
|  |         <Container> | ||||||
|  |             <Typography variant="h1">404</Typography> | ||||||
|  |             <Typography variant="h2">Strona nie została znaleziona</Typography> | ||||||
|  | 
 | ||||||
|  |             <Box my={ 4 }> | ||||||
|  |                 <Divider variant="fullWidth"/> | ||||||
|  |             </Box> | ||||||
|  | 
 | ||||||
|  |             <Button to={ route("home") } color="primary" component={ RouterLink }>strona główna</Button> | ||||||
|  |         </Container> | ||||||
|  |     </Page> | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export default NotFoundPage; | ||||||
							
								
								
									
										3
									
								
								src/pages/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/pages/index.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | |||||||
|  | export * from "./internship/proposal"; | ||||||
|  | export * from "./errors/not-found" | ||||||
|  | export * from "./main" | ||||||
							
								
								
									
										23
									
								
								src/pages/internship/proposal.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/pages/internship/proposal.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | |||||||
|  | import { Page } from "@/pages/base"; | ||||||
|  | import { Container, Link, Typography } from "@material-ui/core"; | ||||||
|  | import { Link as RouterLink } from "react-router-dom"; | ||||||
|  | import { route } from "@/routing"; | ||||||
|  | import { InternshipForm } from "@/forms/Internship"; | ||||||
|  | import React from "react"; | ||||||
|  | 
 | ||||||
|  | export const InternshipProposalPage = () => { | ||||||
|  |     return <Page title="Zgłoszenie praktyki"> | ||||||
|  |         <Page.Header maxWidth="md"> | ||||||
|  |             <Page.Breadcrumbs> | ||||||
|  |                 <Link component={ RouterLink } to={ route("home") }>Moja praktyka</Link> | ||||||
|  |                 <Typography color="textPrimary">Zgłoszenie praktyki</Typography> | ||||||
|  |             </Page.Breadcrumbs> | ||||||
|  |             <Page.Title>Zgłoszenie praktyki</Page.Title> | ||||||
|  |         </Page.Header> | ||||||
|  |         <Container maxWidth={ "md" }> | ||||||
|  |             <InternshipForm/> | ||||||
|  |         </Container> | ||||||
|  |     </Page> | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export default InternshipProposalPage; | ||||||
| @ -1,65 +1,58 @@ | |||||||
| import React from "react"; | import React, { useMemo } from "react"; | ||||||
| import { Page } from "@/pages/base"; | import { Page } from "@/pages/base"; | ||||||
| import { Container, Typography, Button, Divider, Box, Stepper, Step, StepLabel, StepContent, Link } from "@material-ui/core"; | import { Box, Button, Container, Step as StepperStep, StepContent, StepLabel, Stepper, StepProps as StepperStepProps, Typography } from "@material-ui/core"; | ||||||
| import { InternshipForm } from "@/forms/Internship"; |  | ||||||
| import { Link as RouterLink } from "react-router-dom"; | import { Link as RouterLink } from "react-router-dom"; | ||||||
| import { route } from "@/routing"; | import { route } from "@/routing"; | ||||||
| import { useTranslation } from "react-i18next"; | import { useTranslation } from "react-i18next"; | ||||||
|  | import moment, { Moment } from "moment"; | ||||||
|  | 
 | ||||||
|  | type StepProps = StepperStepProps & { | ||||||
|  |     until?: Moment; | ||||||
|  |     completedOn?: Moment; | ||||||
|  |     label: string; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const now = moment(); | ||||||
|  | 
 | ||||||
|  | const Step = ({ until, label, completedOn, children, completed, ...props }: StepProps) => { | ||||||
|  |     const { t } = useTranslation(); | ||||||
|  | 
 | ||||||
|  |     const isLate = useMemo(() => until?.isBefore(completedOn || now), [completedOn, until]); | ||||||
|  |     const left   = useMemo(() => moment.duration(now.diff(until)), [until]); | ||||||
|  | 
 | ||||||
|  |     return <StepperStep { ...props } completed={ completed || !!completedOn }> | ||||||
|  |         <StepLabel> | ||||||
|  |             { label } | ||||||
|  |             { until && <Box> | ||||||
|  |                 <Typography variant="subtitle2" color="textSecondary"> | ||||||
|  |                     { t('until', { date: until.format("DD MMMM YYYY") }) } | ||||||
|  |                     { isLate && <Typography color="error" display="inline" variant="body2"> - { t('late', { by: moment.duration(now.diff(until)).humanize() }) }</Typography> } | ||||||
|  |                     { !isLate && !completed && <Typography display="inline" variant="body2"> - { t('left', { left: left.humanize() }) }</Typography> } | ||||||
|  |                 </Typography> | ||||||
|  |             </Box> } | ||||||
|  |         </StepLabel> | ||||||
|  |         { children && <StepContent>{ children }</StepContent> } | ||||||
|  |     </StepperStep> | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| export const MainPage = () => { | export const MainPage = () => { | ||||||
|     const { t } = useTranslation(); |     const { t } = useTranslation(); | ||||||
| 
 | 
 | ||||||
|     return <Page my={6}> |     return <Page my={ 6 }> | ||||||
|         <Container> |         <Container> | ||||||
|             <Typography variant="h2">{ t("sections.my-internship.header") }</Typography> |             <Typography variant="h2">{ t("sections.my-internship.header") }</Typography> | ||||||
|             <Stepper orientation="vertical" nonLinear> |             <Stepper orientation="vertical" nonLinear> | ||||||
|                 <Step completed active={false}> |                 <Step label={ t('steps.personal-data.header') } until={ moment("2020-07-01") }/> | ||||||
|                     <StepLabel> |                 <Step label={ t('steps.internship-proposal.header') }> | ||||||
|                         { t('steps.personal-data.header') } |  | ||||||
|                         <Box> |  | ||||||
|                             <Typography variant="subtitle2" color="textSecondary">{ t('until', { date: '05.07.2020' }) }</Typography> |  | ||||||
|                         </Box> |  | ||||||
|                     </StepLabel> |  | ||||||
|                 </Step> |  | ||||||
|                 <Step active> |  | ||||||
|                     <StepLabel>{ t('steps.internship-proposal.header')}</StepLabel> |  | ||||||
|                     <StepContent> |  | ||||||
|                     <Button to={ route("internship_proposal") } variant="contained" color="primary" component={ RouterLink }> |                     <Button to={ route("internship_proposal") } variant="contained" color="primary" component={ RouterLink }> | ||||||
|                         { t('steps.internship-proposal.form') } |                         { t('steps.internship-proposal.form') } | ||||||
|                     </Button> |                     </Button> | ||||||
|                     </StepContent> |  | ||||||
|                 </Step> |                 </Step> | ||||||
|  |                 <Step label={ t('steps.plan.header') } until={ moment("2020-07-22") }/> | ||||||
|  |                 <Step label={ t('steps.insurance.header') }/> | ||||||
|  |                 <Step label={ t('steps.report.header') }/> | ||||||
|  |                 <Step label={ t('steps.grade.header') }/> | ||||||
|             </Stepper> |             </Stepper> | ||||||
|         </Container> |         </Container> | ||||||
|     </Page> |     </Page> | ||||||
| } | } | ||||||
| 
 |  | ||||||
| export const InternshipFormPage = () => { |  | ||||||
|     return <Page title="Zgłoszenie praktyki"> |  | ||||||
|         <Page.Header maxWidth="md"> |  | ||||||
|             <Page.Breadcrumbs> |  | ||||||
|                 <Link component={ RouterLink } to={ route("home") }>Moja praktyka</Link> |  | ||||||
|                 <Typography color="textPrimary">Zgłoszenie praktyki</Typography> |  | ||||||
|             </Page.Breadcrumbs> |  | ||||||
|             <Page.Title>Zgłoszenie praktyki</Page.Title> |  | ||||||
|         </Page.Header> |  | ||||||
|         <Container maxWidth={"md"}> |  | ||||||
|             <InternshipForm /> |  | ||||||
|         </Container> |  | ||||||
|     </Page> |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export const NotFoundPage = () => { |  | ||||||
|     return <Page title="Strona nie została znaleziona"> |  | ||||||
|         <Container> |  | ||||||
|             <Typography variant="h1">404</Typography> |  | ||||||
|             <Typography variant="h2">Strona nie została znaleziona</Typography> |  | ||||||
| 
 |  | ||||||
|             <Box my={ 4 }> |  | ||||||
|                 <Divider variant="fullWidth" /> |  | ||||||
|             </Box> |  | ||||||
| 
 |  | ||||||
|             <Button to={ route("home") } color="primary" component={ RouterLink }>strona główna</Button> |  | ||||||
|         </Container> |  | ||||||
|     </Page> |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -1,6 +1,8 @@ | |||||||
| import React, { ReactComponentElement } from "react"; | import React, { ReactComponentElement } from "react"; | ||||||
| import { InternshipFormPage, MainPage, NotFoundPage } from "@/pages/main"; | import { MainPage } from "@/pages/main"; | ||||||
| import { RouteProps } from "react-router-dom"; | import { RouteProps } from "react-router-dom"; | ||||||
|  | import { InternshipProposalPage } from "@/pages/internship/proposal"; | ||||||
|  | import { NotFoundPage } from "@/pages/errors/not-found"; | ||||||
| 
 | 
 | ||||||
| type Route = { | type Route = { | ||||||
|     name?: string; |     name?: string; | ||||||
| @ -8,18 +10,18 @@ type Route = { | |||||||
| } & RouteProps; | } & RouteProps; | ||||||
| 
 | 
 | ||||||
| export const routes: Route[] = [ | export const routes: Route[] = [ | ||||||
|     { name: "home", path: "/", exact: true, content: () => <MainPage /> }, |     { name: "home", path: "/", exact: true, content: () => <MainPage/> }, | ||||||
| 
 | 
 | ||||||
|     { name: "internship_proposal", path: "/internship/proposal", exact: true, content: () => <InternshipFormPage /> }, |     { name: "internship_proposal", path: "/internship/proposal", exact: true, content: () => <InternshipProposalPage/> }, | ||||||
| 
 | 
 | ||||||
|     // fallback route for 404 pages
 |     // fallback route for 404 pages
 | ||||||
|     { path: "*", content: () => <NotFoundPage /> } |     { name: "fallback", path: "*", content: () => <NotFoundPage/> } | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| const routeNameMap = new Map(routes.filter(({name}) => !!name).map(({ name, path }) => [ name, path instanceof Array ? path[0] : path ])) as Map<string, string> | const routeNameMap = new Map(routes.filter(({ name }) => !!name).map(({ name, path }) => [name, path instanceof Array ? path[0] : path])) as Map<string, string> | ||||||
| 
 | 
 | ||||||
| export function route(name: string, params: { [param: string]: string } = { }) { | export function route(name: string, params: { [param: string]: string } = {}) { | ||||||
|     const url = routeNameMap.get(name) || ""; |     const url = routeNameMap.get(name) || ""; | ||||||
| 
 | 
 | ||||||
|     return Object.entries(params).reduce((url, [name, value]) => url.replace(`:${name}`, value), url); |     return Object.entries(params).reduce((url, [name, value]) => url.replace(`:${ name }`, value), url); | ||||||
| } | } | ||||||
|  | |||||||
| @ -4,6 +4,8 @@ logout: logout | |||||||
| logged-in-as: logged in as <1>{{ name }}</1> | logged-in-as: logged in as <1>{{ name }}</1> | ||||||
| 
 | 
 | ||||||
| until: until {{ date }} | until: until {{ date }} | ||||||
|  | late: late by {{ by }} | ||||||
|  | left: '{{ left }} left' | ||||||
| 
 | 
 | ||||||
| sections: | sections: | ||||||
|   my-internship: |   my-internship: | ||||||
| @ -11,7 +13,9 @@ sections: | |||||||
| 
 | 
 | ||||||
| steps: | steps: | ||||||
|   personal-data: |   personal-data: | ||||||
|     header: "Personal data" |     header: "Fill personal data" | ||||||
|   internship-proposal: |   internship-proposal: | ||||||
|     header: "Internship proposal" |     header: "Internship proposal" | ||||||
|     form: "Internship proposal form" |     form: "Internship proposal form" | ||||||
|  |   plan: | ||||||
|  |     header: "Individual Internship Plan" | ||||||
|  | |||||||
| @ -4,6 +4,8 @@ logout: wyloguj się | |||||||
| logged-in-as: zalogowany jako <1>{{ name }}</1> | logged-in-as: zalogowany jako <1>{{ name }}</1> | ||||||
| 
 | 
 | ||||||
| until: do {{ date }} | until: do {{ date }} | ||||||
|  | late: '{{ by }} spóźnienia' | ||||||
|  | left: jeszcze {{ left }} | ||||||
| 
 | 
 | ||||||
| sections: | sections: | ||||||
|   my-internship: |   my-internship: | ||||||
| @ -15,3 +17,11 @@ steps: | |||||||
|   internship-proposal: |   internship-proposal: | ||||||
|     header: "Zgłoszenie praktyki" |     header: "Zgłoszenie praktyki" | ||||||
|     form: "Formularz zgłaszania praktyki" |     form: "Formularz zgłaszania praktyki" | ||||||
|  |   plan: | ||||||
|  |     header: "Indywidualny Program Praktyki" | ||||||
|  |   report: | ||||||
|  |     header: "Raport z praktyki" | ||||||
|  |   grade: | ||||||
|  |     header: "Ocena z praktyki" | ||||||
|  |   insurance: | ||||||
|  |     header: "Ubezpieczenie NWW" | ||||||
|  | |||||||
| @ -1043,6 +1043,13 @@ | |||||||
|     react-is "^16.8.0" |     react-is "^16.8.0" | ||||||
|     react-transition-group "^4.4.0" |     react-transition-group "^4.4.0" | ||||||
| 
 | 
 | ||||||
|  | "@material-ui/icons@^4.9.1": | ||||||
|  |   version "4.9.1" | ||||||
|  |   resolved "https://registry.yarnpkg.com/@material-ui/icons/-/icons-4.9.1.tgz#fdeadf8cb3d89208945b33dbc50c7c616d0bd665" | ||||||
|  |   integrity sha512-GBitL3oBWO0hzBhvA9KxqcowRUsA0qzwKkURyC8nppnC3fw54KPKZ+d4V1Eeg/UnDRSzDaI9nGCdel/eh9AQMg== | ||||||
|  |   dependencies: | ||||||
|  |     "@babel/runtime" "^7.4.4" | ||||||
|  | 
 | ||||||
| "@material-ui/lab@^4.0.0-alpha.55": | "@material-ui/lab@^4.0.0-alpha.55": | ||||||
|   version "4.0.0-alpha.55" |   version "4.0.0-alpha.55" | ||||||
|   resolved "https://registry.yarnpkg.com/@material-ui/lab/-/lab-4.0.0-alpha.55.tgz#82a850dc59654e04ee3a31be1b34eb3bf64d5585" |   resolved "https://registry.yarnpkg.com/@material-ui/lab/-/lab-4.0.0-alpha.55.tgz#82a850dc59654e04ee3a31be1b34eb3bf64d5585" | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user