feature_state #6
75
src/components/proposalPreview.tsx
Normal file
75
src/components/proposalPreview.tsx
Normal 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>
|
||||||
|
}
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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");
|
||||||
}
|
}
|
||||||
|
@ -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";
|
|
||||||
|
@ -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;
|
||||||
|
@ -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 }
|
||||||
|
@ -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
|
||||||
|
@ -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
32
src/utils/numbers.ts
Normal 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;
|
||||||
|
}
|
@ -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"
|
||||||
|
Loading…
Reference in New Issue
Block a user