feature_state #6

Closed
kadet wants to merge 11 commits from feature_state into master
11 changed files with 177 additions and 16 deletions
Showing only changes of commit 5316630a29 - Show all commits

View File

@ -0,0 +1,75 @@
import { Internship } from "@/data";
import React from "react";
import { Paper, PaperProps, Typography, TypographyProps } from "@material-ui/core";
import { useTranslation } from "react-i18next";
import { createStyles, makeStyles } from "@material-ui/core/styles";
import classNames from "classnames";
export type ProposalPreviewProps = {
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),
marginTop: theme.spacing(3)
}
}))
const Section = ({ children, ...props }: PaperProps) => {
const classes = useSectionStyles();
return <Paper {...props} className={ classNames(classes.root, props.className ) }>
{ children }
</Paper>
}
export const ProposalPreview = ({ proposal }: ProposalPreviewProps) => {
const { t } = useTranslation();
return <div className="proposal">
<Typography className="proposal__primary">{ proposal.intern.name } { proposal.intern.surname }</Typography>
<Typography className="proposal__secondary">
{ t('internship.intern.semester', { semester: proposal.intern.semester }) }
{ ", " }
{ t('internship.intern.album', { album: proposal.intern.albumNumber }) }
</Typography>
<Section>
<Label>{ t('internship.sections.kind') }</Label>
<Typography className="proposal__primary">{ proposal.type }</Typography>
</Section>
<Section>
<Label>{ t('internship.sections.duration') }</Label>
<Typography className="proposal__primary">
{ t('internship.date-range', { start: proposal.startDate, end: proposal.endDate }) }
</Typography>
<Typography className="proposal__secondary">
{ t('internship.duration', { duration: 0 })}
{ ", " }
{ t('internship.hours', { hours: proposal.hours })}
</Typography>
</Section>
<Section>
<Label>{ t('internship.sections.place') }</Label>
<Typography className="proposal__primary">
{ proposal.company.name }
</Typography>
<Typography className="proposal__secondary">
NIP: { proposal.company.nip }
</Typography>
</Section>
<Section>
<Label>{ t('internship.office') }</Label>
<Typography className="proposal__primary">{ t('internship.address.city', proposal.office.address) }</Typography>
<Typography className="proposal__secondary">{ t('internship.address.street', proposal.office.address) }</Typography>
</Section>
</div>
}

View File

@ -182,13 +182,13 @@ export const InternshipForm: React.FunctionComponent<InternshipFormProps> = prop
return ( return (
<div className="internship-form"> <div className="internship-form">
<Typography variant="h3" className="section-header">Dane osoby odbywającej praktykę</Typography> <Typography variant="h3" className="section-header">{ t('internship.intern-info') }</Typography>
<StudentForm student={ sampleStudent }/> <StudentForm student={ sampleStudent }/>
<Typography variant="h3" className="section-header">Rodzaj i program praktyki</Typography> <Typography variant="h3" className="section-header">{ t('internship.kind' )}</Typography>
<InternshipProgramForm internship={ internship } onChange={ setInternship }/> <InternshipProgramForm internship={ internship } onChange={ setInternship }/>
<Typography variant="h3" className="section-header">Czas trwania praktyki</Typography> <Typography variant="h3" className="section-header">{ t('internship.duration') }</Typography>
<InternshipDurationForm internship={ internship } onChange={ setInternship }/> <InternshipDurationForm internship={ internship } onChange={ setInternship }/>
<Typography variant="h3" className="section-header">Miejsce odbywania praktyki</Typography> <Typography variant="h3" className="section-header">{ t('internship.place') }</Typography>
<CompanyForm internship={ internship } onChange={ setInternship }/> <CompanyForm internship={ internship } onChange={ setInternship }/>
<Actions> <Actions>
<Button variant="contained" color="primary" onClick={ handleSubmitConfirmation }>{ t("confirm") }</Button> <Button variant="contained" color="primary" onClick={ handleSubmitConfirmation }>{ t("confirm") }</Button>

View File

@ -32,7 +32,7 @@ export const PlanForm = () => {
</Button> </Button>
</Grid> </Grid>
<Grid item> <Grid item>
<DropzoneArea acceptedFiles={["image/*", "application/x-pdf"]} filesLimit={ 1 } dropzoneText={ t("dropzone") }/> <DropzoneArea acceptedFiles={["image/*", "application/pdf"]} filesLimit={ 1 } dropzoneText={ t("dropzone") }/>
<FormHelperText>{ t('forms.plan.dropzone-help') }</FormHelperText> <FormHelperText>{ t('forms.plan.dropzone-help') }</FormHelperText>
</Grid> </Grid>
<Grid item> <Grid item>

View File

@ -5,6 +5,7 @@ import I18nextBrowserLanguageDetector from "i18next-browser-languagedetector";
import "moment/locale/pl" import "moment/locale/pl"
import "moment/locale/en-gb" import "moment/locale/en-gb"
import moment, { isDuration, isMoment } from "moment"; import moment, { isDuration, isMoment } from "moment";
import { convertToRoman } from "@/utils/numbers";
const resources = { const resources = {
en: { en: {
@ -24,6 +25,10 @@ i18n
interpolation: { interpolation: {
escapeValue: false, escapeValue: false,
format: (value, format, lng) => { format: (value, format, lng) => {
if (typeof value === "number" && format == "roman") {
return convertToRoman(value);
}
if (isMoment(value)) { if (isMoment(value)) {
return value.locale(lng || "pl").format(format || "DD MMM YYYY"); return value.locale(lng || "pl").format(format || "DD MMM YYYY");
} }

View File

@ -1,7 +1,3 @@
export * from "./internship/proposal"; export * from "./internship/proposal";
export * from "./errors/not-found" export * from "./errors/not-found"
export * from "./main" export * from "./main"
export { ProposalStep } from "@/pages/steps/proposal";
export { ProposalComment } from "@/pages/steps/proposal";
export { ProposalActions } from "@/pages/steps/proposal";
export { ProposalStatus } from "@/pages/steps/proposal";

View File

@ -4,9 +4,15 @@ import { Link as RouterLink } from "react-router-dom";
import { route } from "@/routing"; import { route } from "@/routing";
import { InternshipForm } from "@/forms/internship"; import { InternshipForm } from "@/forms/internship";
import React from "react"; import React from "react";
import { ProposalComment } from "@/pages"; import { ProposalComment } from "@/pages/steps/proposal";
import { useTranslation } from "react-i18next";
import { ProposalPreview } from "@/components/proposalPreview";
import { useSelector } from "react-redux";
import { Internship } from "@/data";
import { AppState } from "@/state/reducer";
import { internshipSerializationTransformer } from "@/serialization";
export const InternshipProposalPage = () => { export const InternshipProposalFormPage = () => {
return <Page title="Zgłoszenie praktyki"> return <Page title="Zgłoszenie praktyki">
<Page.Header maxWidth="md"> <Page.Header maxWidth="md">
<Page.Breadcrumbs> <Page.Breadcrumbs>
@ -22,4 +28,23 @@ export const InternshipProposalPage = () => {
</Page> </Page>
} }
export default InternshipProposalPage; export const InternshipProposalPreviewPage = () => {
const { t } = useTranslation();
const proposal = useSelector<AppState, Internship | null>(state => state.proposal.proposal && internshipSerializationTransformer.reverseTransform(state.proposal.proposal));
return <Page title={ t("") }>
<Page.Header maxWidth="md">
<Page.Breadcrumbs>
<Link component={ RouterLink } to={ route("home") }>Moja praktyka</Link>
<Typography color="textPrimary">Podgląd zgłoszenia</Typography>
</Page.Breadcrumbs>
<Page.Title>Moje zgłoszenie</Page.Title>
</Page.Header>
<Container maxWidth={ "md" }>
<ProposalComment />
{ proposal && <ProposalPreview proposal={ proposal } /> }
</Container>
</Page>
}
export default InternshipProposalFormPage;

View File

@ -18,7 +18,11 @@ const ProposalActions = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const ReviewAction = (props: ButtonProps) => const ReviewAction = (props: ButtonProps) =>
<Button startIcon={ <FileFind/> } { ...props }>{ t('review') }</Button> <Button startIcon={ <FileFind/> }
component={ RouterLink } to={ route("internship_proposal_preview") }
{ ...props as any }>
{ t('review') }
</Button>
const FormAction = ({ children = t('steps.internship-proposal.form'), ...props }: ButtonProps) => const FormAction = ({ children = t('steps.internship-proposal.form'), ...props }: ButtonProps) =>
<Button to={ route("internship_proposal") } variant="contained" color="primary" component={ RouterLink } <Button to={ route("internship_proposal") } variant="contained" color="primary" component={ RouterLink }

View File

@ -1,7 +1,7 @@
import React, { ReactComponentElement } from "react"; import React, { ReactComponentElement } from "react";
import { MainPage } 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 { 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";
@ -13,7 +13,8 @@ type Route = {
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: () => <InternshipProposalPage/> }, { name: "internship_proposal", path: "/internship/proposal", exact: true, content: () => <InternshipProposalFormPage/> },
{ 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/> },
// fallback route for 404 pages // fallback route for 404 pages

View File

@ -12,3 +12,7 @@
font-weight: 400; font-weight: 400;
line-height: 1.5; line-height: 1.5;
} }
.proposal__primary {
font-size: 1.675rem;
}

32
src/utils/numbers.ts Normal file
View File

@ -0,0 +1,32 @@
const roman = {
M: 1000,
CM: 900,
D: 500,
CD: 400,
C: 100,
XC: 90,
L: 50,
XL: 40,
X: 10,
IX: 9,
V: 5,
IV: 4,
I: 1
};
type RomanLiteral = keyof typeof roman;
// shamefully stolen from https://stackoverflow.com/questions/9083037/convert-a-number-into-a-roman-numeral-in-javascript
export function convertToRoman(number: number) {
let result = '';
for (const i in roman) {
const q = Math.floor(number / roman[i as RomanLiteral]);
number -= q * roman[i as RomanLiteral];
result += i.repeat(q);
}
return result;
}

View File

@ -20,6 +20,7 @@ comments: Zgłoszone uwagi
send-again: wyślij ponownie send-again: wyślij ponownie
cancel: anuluj cancel: anuluj
dropzone: "Przeciągnij i upuść plik bądź kliknij, aby wybrać" dropzone: "Przeciągnij i upuść plik bądź kliknij, aby wybrać"
sections: sections:
@ -31,7 +32,7 @@ forms:
send-confirmation: > send-confirmation: >
Po wysłaniu zgłoszenia nie będzie możliwości jego zmiany do czasu zweryfikowania go przez pełnomocnika ds. Twojego Po wysłaniu zgłoszenia nie będzie możliwości jego zmiany do czasu zweryfikowania go przez pełnomocnika ds. Twojego
kierunku. Czy na pewno chcesz wysłać zgłoszenie praktyki w tej formie? kierunku. Czy na pewno chcesz wysłać zgłoszenie praktyki w tej formie?
program: plan:
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
@ -51,6 +52,24 @@ submission:
declined: "do poprawy" declined: "do poprawy"
draft: "wersja robocza" draft: "wersja robocza"
internship:
intern:
semester: semestr {{ semester, roman }}
album: "numer albumu {{ album }}"
date-range: "{{ start, DD MMMM YYYY }} - {{ end, DD MMMM YYYY }}"
duration: "{{ duration, humanize }} tygodni"
hours: "{{ hours }} godzin"
office: "Oddział / adres"
address:
city: "{{ city }}, {{ country }}"
street: "{{ postalCode }}, {{ street }} {{ building }}"
sections:
intern-info: "Dane osoby odbywającej praktykę"
duration: "Czas trwania praktyki"
place: "Miejsce odbywania praktyki"
kind: "Rodzaj i program praktyki"
steps: steps:
personal-data: personal-data:
header: "Uzupełnienie informacji" header: "Uzupełnienie informacji"