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