Add edition registration
This commit is contained in:
		
							parent
							
								
									b4a7e33fa4
								
							
						
					
					
						commit
						2f0c0a38f2
					
				| @ -1,6 +1,9 @@ | |||||||
| import { axios } from "@/api/index"; | import { axios } from "@/api/index"; | ||||||
|  | import { Edition } from "@/data/edition"; | ||||||
|  | import { sampleEdition } from "@/provider/dummy"; | ||||||
| 
 | 
 | ||||||
| const EDITIONS_ENDPOINT = "/editions"; | const EDITIONS_ENDPOINT = "/editions"; | ||||||
|  | const EDITION_INFO_ENDPOINT = "/editions/:key"; | ||||||
| const REGISTER_ENDPOINT = "/register"; | const REGISTER_ENDPOINT = "/register"; | ||||||
| 
 | 
 | ||||||
| export async function editions() { | export async function editions() { | ||||||
| @ -18,3 +21,12 @@ export async function join(key: string): Promise<boolean> { | |||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | // MOCK
 | ||||||
|  | export async function get(key: string): Promise<Edition | null> { | ||||||
|  |     if (key == "inf2020") { | ||||||
|  |         return sampleEdition; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return null; | ||||||
|  | } | ||||||
|  | |||||||
| @ -3,12 +3,11 @@ import store from "@/state/store" | |||||||
| import { AppState } from "@/state/reducer"; | import { AppState } from "@/state/reducer"; | ||||||
| import { UserState } from "@/state/reducer/user"; | import { UserState } from "@/state/reducer/user"; | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| import * as user from "./user"; | import * as user from "./user"; | ||||||
| import * as edition from "./edition"; | import * as edition from "./edition"; | ||||||
| 
 | 
 | ||||||
| export const axios = Axios.create({ | export const axios = Axios.create({ | ||||||
|     baseURL: "http://system-praktyk-front.localhost:3000/api/", |     baseURL: process.env.API_BASE_URL || "https://system-praktyk-front.stg.kadet.net/api/", | ||||||
| }) | }) | ||||||
| 
 | 
 | ||||||
| axios.interceptors.request.use(config => { | axios.interceptors.request.use(config => { | ||||||
|  | |||||||
| @ -1,34 +1,16 @@ | |||||||
| import { Internship, internshipTypeLabels } from "@/data"; | import { Internship, internshipTypeLabels } from "@/data"; | ||||||
| import React from "react"; | import React from "react"; | ||||||
| import { Paper, PaperProps, Typography, TypographyProps } from "@material-ui/core"; | import { Typography } from "@material-ui/core"; | ||||||
| import { useTranslation } from "react-i18next"; | import { useTranslation } from "react-i18next"; | ||||||
| import { createStyles, makeStyles } from "@material-ui/core/styles"; |  | ||||||
| import classNames from "classnames"; | import classNames from "classnames"; | ||||||
| import { useVerticalSpacing } from "@/styles"; | import { useVerticalSpacing } from "@/styles"; | ||||||
| import moment from "moment"; | import moment from "moment"; | ||||||
|  | import { Label, Section } from "@/components/section"; | ||||||
| 
 | 
 | ||||||
| export type ProposalPreviewProps = { | export type ProposalPreviewProps = { | ||||||
|     proposal: Internship; |     proposal: Internship; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const Label = ({ children }: TypographyProps) => { |  | ||||||
|     return <Typography variant="subtitle2" className="proposal__header">{ children }</Typography> |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| const useSectionStyles = makeStyles(theme => createStyles({ |  | ||||||
|     root: { |  | ||||||
|         padding: theme.spacing(2), |  | ||||||
|     } |  | ||||||
| })) |  | ||||||
| 
 |  | ||||||
| const Section = ({ children, ...props }: PaperProps) => { |  | ||||||
|     const classes = useSectionStyles(); |  | ||||||
| 
 |  | ||||||
|     return <Paper {...props} className={ classNames(classes.root, props.className ) }> |  | ||||||
|         { children } |  | ||||||
|     </Paper> |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export const ProposalPreview = ({ proposal }: ProposalPreviewProps) => { | export const ProposalPreview = ({ proposal }: ProposalPreviewProps) => { | ||||||
|     const { t } = useTranslation(); |     const { t } = useTranslation(); | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										22
									
								
								src/components/section.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/components/section.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | |||||||
|  | import { createStyles, makeStyles } from "@material-ui/core/styles"; | ||||||
|  | import { Paper, PaperProps, Typography, TypographyProps } from "@material-ui/core"; | ||||||
|  | import classNames from "classnames"; | ||||||
|  | import React from "react"; | ||||||
|  | 
 | ||||||
|  | const useSectionStyles = makeStyles(theme => createStyles({ | ||||||
|  |     root: { | ||||||
|  |         padding: theme.spacing(2), | ||||||
|  |     } | ||||||
|  | })) | ||||||
|  | 
 | ||||||
|  | export const Section = ({ children, ...props }: PaperProps) => { | ||||||
|  |     const classes = useSectionStyles(); | ||||||
|  | 
 | ||||||
|  |     return <Paper { ...props } className={ classNames(classes.root, props.className) }> | ||||||
|  |         { children } | ||||||
|  |     </Paper> | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export const Label = ({ children }: TypographyProps) => { | ||||||
|  |     return <Typography variant="subtitle2" className="proposal__header">{ children }</Typography> | ||||||
|  | } | ||||||
| @ -1,6 +1,8 @@ | |||||||
| import { Moment } from "moment"; | import { Moment } from "moment"; | ||||||
|  | import { Course } from "@/data/course"; | ||||||
| 
 | 
 | ||||||
| export type Edition = { | export type Edition = { | ||||||
|  |     course: Course; | ||||||
|     startDate: Moment; |     startDate: Moment; | ||||||
|     endDate: Moment; |     endDate: Moment; | ||||||
|     proposalDeadline: Moment; |     proposalDeadline: Moment; | ||||||
|  | |||||||
| @ -1,29 +1,59 @@ | |||||||
| import React, { useState } from "react"; | import React, { useEffect, useState } from "react"; | ||||||
| import { Page } from "@/pages/base"; | import { Page } from "@/pages/base"; | ||||||
| import { useTranslation } from "react-i18next"; | import { useTranslation } from "react-i18next"; | ||||||
| import { Button, Container, TextField } from "@material-ui/core"; | import { Button, Container, TextField, Typography } from "@material-ui/core"; | ||||||
| 
 | 
 | ||||||
| import api from "@/api"; | import api from "@/api"; | ||||||
|  | import { useVerticalSpacing } from "@/styles"; | ||||||
|  | import { Edition } from "@/data/edition"; | ||||||
|  | import { Label, Section } from "@/components/section"; | ||||||
|  | import { Alert } from "@material-ui/lab"; | ||||||
| 
 | 
 | ||||||
| export const RegisterEditionPage = () => { | export const RegisterEditionPage = () => { | ||||||
|     const { t } = useTranslation(); |     const { t } = useTranslation(); | ||||||
| 
 | 
 | ||||||
|     const [key, setKey] = useState<string>(""); |     const [key, setKey] = useState<string>(""); | ||||||
|  |     const [edition, setEdition] = useState<Edition | null>(null); | ||||||
|  | 
 | ||||||
|  |     const classes = useVerticalSpacing(3); | ||||||
|  | 
 | ||||||
|  |     useEffect(() => { | ||||||
|  |         (async () => setEdition(await api.edition.get(key)))(); | ||||||
|  |     }, [ key ]) | ||||||
| 
 | 
 | ||||||
|     const handleRegister = () => { |     const handleRegister = () => { | ||||||
|  |         try { | ||||||
|             api.edition.join(key); |             api.edition.join(key); | ||||||
|  |         } catch (error) { | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const handleKeyChange = (ev: React.ChangeEvent<HTMLInputElement>) => { | ||||||
|  |         setKey(ev.currentTarget.value); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return <Page> |     return <Page> | ||||||
|         <Page.Header maxWidth="md"> |         <Page.Header maxWidth="md"> | ||||||
|             <Page.Title>{ t("edition.register") }</Page.Title> |             <Page.Title>{ t("pages.edition.header") }</Page.Title> | ||||||
|         </Page.Header> |         </Page.Header> | ||||||
| 
 | 
 | ||||||
|         <Container maxWidth="md"> |         <Container maxWidth="md" className={ classes.root }> | ||||||
|             <TextField label={ t("edition.key") } fullWidth |             <TextField label={ t("forms.edition-register.fields.key") } fullWidth | ||||||
|                        onChange={ (ev: React.ChangeEvent<HTMLInputElement>) => setKey(ev.currentTarget.value) } |                        onChange={ handleKeyChange } | ||||||
|                        value={ key } /> |                        value={ key } /> | ||||||
|             <Button onClick={ handleRegister }>{ t("edition.register") }</Button> |             { edition | ||||||
|  |                 ? <Section> | ||||||
|  |                     <Label>{ t("forms.edition-register.edition" ) }</Label> | ||||||
|  |                     <Typography className="proposal__primary">{ edition.course.name }</Typography> | ||||||
|  |                     <Typography className="proposal__secondary"> | ||||||
|  |                         { t('internship.date-range', { start: edition.startDate, end: edition.endDate }) } | ||||||
|  |                     </Typography> | ||||||
|  |                 </Section> | ||||||
|  |                 : <Alert severity="warning">{ t("forms.edition-register.edition-not-found") }</Alert> | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             <Button onClick={ handleRegister } variant="contained" color="primary" disabled={ !edition }>{ t("forms.edition-register.register") }</Button> | ||||||
|         </Container> |         </Container> | ||||||
|     </Page> |     </Page> | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,9 +1,11 @@ | |||||||
| import { Edition } from "@/data/edition"; | import { Edition } from "@/data/edition"; | ||||||
| import moment from "moment"; | import moment from "moment"; | ||||||
|  | import { sampleCourse } from "@/provider/dummy/student"; | ||||||
| 
 | 
 | ||||||
| export const sampleEdition: Edition = { | export const sampleEdition: Edition = { | ||||||
|     startDate: moment("2020-07-01"), |     startDate: moment("2020-07-01"), | ||||||
|     endDate: moment("2020-09-30"), |     endDate: moment("2020-09-30"), | ||||||
|     proposalDeadline: moment("2020-07-31"), |     proposalDeadline: moment("2020-07-31"), | ||||||
|     minimumInternshipHours: 40, |     minimumInternshipHours: 40, | ||||||
|  |     course: sampleCourse, | ||||||
| } | } | ||||||
|  | |||||||
| @ -33,8 +33,14 @@ export const routes: Route[] = [ | |||||||
| 
 | 
 | ||||||
| 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 type URLParams = { [param: string]: string }; | ||||||
|  | 
 | ||||||
|  | export const prepare = (url: string, params: URLParams) => Object | ||||||
|  |     .entries(params) | ||||||
|  |     .reduce((url, [name, value]) => url.replace(`:${ name }`, value), url) | ||||||
|  | 
 | ||||||
|  | export function route(name: string, params: URLParams = {}) { | ||||||
|     const url = routeNameMap.get(name) || ""; |     const url = routeNameMap.get(name) || ""; | ||||||
| 
 | 
 | ||||||
|     return Object.entries(params).reduce((url, [name, value]) => url.replace(`:${ name }`, value), url); |     return prepare(url, params) | ||||||
| } | } | ||||||
|  | |||||||
| @ -32,6 +32,8 @@ pages: | |||||||
|     header: "Moja praktyka" |     header: "Moja praktyka" | ||||||
|   proposal-form: |   proposal-form: | ||||||
|     header: "Zgłoszenie praktyki" |     header: "Zgłoszenie praktyki" | ||||||
|  |   edition: | ||||||
|  |     header: "Zapisz się do edycji" | ||||||
| 
 | 
 | ||||||
| forms: | forms: | ||||||
|   internship: |   internship: | ||||||
| @ -67,6 +69,12 @@ forms: | |||||||
|     instructions: > |     instructions: > | ||||||
|       Wypełnij i zeskanuj Indywidualny program Praktyk a następnie wyślij go z pomocą tego formularza. <więcej informacji> |       Wypełnij i zeskanuj Indywidualny program Praktyk a następnie wyślij go z pomocą tego formularza. <więcej informacji> | ||||||
|     dropzone-help: Skan dokumentu w formacie PDF |     dropzone-help: Skan dokumentu w formacie PDF | ||||||
|  |   edition-register: | ||||||
|  |     register: Zapisz się do edycji | ||||||
|  |     edition: Znaleziona edycja | ||||||
|  |     edition-not-found: "Edycja o takim kluczu nie została znaleziona - sprawdź poprawność klucza." | ||||||
|  |     fields: | ||||||
|  |       key: Klucz dostępu do edycji | ||||||
| 
 | 
 | ||||||
| student: | student: | ||||||
|   name: imię |   name: imię | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ const path = require('path'); | |||||||
| const { CleanWebpackPlugin } = require('clean-webpack-plugin'); | const { CleanWebpackPlugin } = require('clean-webpack-plugin'); | ||||||
| const { GenerateSW } = require('workbox-webpack-plugin'); | const { GenerateSW } = require('workbox-webpack-plugin'); | ||||||
| const HtmlWebpackPlugin = require("html-webpack-plugin") | const HtmlWebpackPlugin = require("html-webpack-plugin") | ||||||
|  | const Webpack = require('webpack'); | ||||||
| 
 | 
 | ||||||
| const config = { | const config = { | ||||||
|     entry: { |     entry: { | ||||||
| @ -46,6 +47,7 @@ const config = { | |||||||
|             filename: 'index.html', |             filename: 'index.html', | ||||||
|             template: 'public/index.html' |             template: 'public/index.html' | ||||||
|         }), |         }), | ||||||
|  |         new Webpack.EnvironmentPlugin(['API_BASE_URL']) | ||||||
|     ], |     ], | ||||||
|     devServer: { |     devServer: { | ||||||
|         contentBase: path.resolve("./public/"), |         contentBase: path.resolve("./public/"), | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user