Add FileInfo component
This commit is contained in:
parent
52bda87494
commit
9977f5678c
@ -31,6 +31,7 @@
|
|||||||
"css-loader": "3.4.2",
|
"css-loader": "3.4.2",
|
||||||
"date-holidays": "^1.5.3",
|
"date-holidays": "^1.5.3",
|
||||||
"file-loader": "4.3.0",
|
"file-loader": "4.3.0",
|
||||||
|
"filesize": "^6.1.0",
|
||||||
"formik": "^2.1.5",
|
"formik": "^2.1.5",
|
||||||
"formik-material-ui": "^3.0.0-alpha.0",
|
"formik-material-ui": "^3.0.0-alpha.0",
|
||||||
"html-webpack-plugin": "4.0.0-beta.11",
|
"html-webpack-plugin": "4.0.0-beta.11",
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { axios } from "@/api/index";
|
import { axios } from "@/api/index";
|
||||||
import { InternshipDocument } from "@/api/dto/internship-registration";
|
import { InternshipDocument } from "@/api/dto/internship-registration";
|
||||||
import { prepare } from "@/routing";
|
import { prepare } from "@/routing";
|
||||||
|
import { Identifiable } from "@/data";
|
||||||
|
|
||||||
export enum UploadType {
|
export enum UploadType {
|
||||||
Ipp = "IppScan",
|
Ipp = "IppScan",
|
||||||
@ -8,6 +9,12 @@ export enum UploadType {
|
|||||||
Insurance = "NnwInsurance",
|
Insurance = "NnwInsurance",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DocumentFileInfo extends Identifiable {
|
||||||
|
filename: string;
|
||||||
|
size: number;
|
||||||
|
mime: string;
|
||||||
|
}
|
||||||
|
|
||||||
const CREATE_DOCUMENT_ENDPOINT = '/document';
|
const CREATE_DOCUMENT_ENDPOINT = '/document';
|
||||||
const DOCUMENT_UPLOAD_ENDPOINT = '/document/:id/scan';
|
const DOCUMENT_UPLOAD_ENDPOINT = '/document/:id/scan';
|
||||||
|
|
||||||
@ -23,3 +30,8 @@ export async function upload(document: InternshipDocument, file: File) {
|
|||||||
const response = await axios.put(prepare(DOCUMENT_UPLOAD_ENDPOINT, { id: document.id as string }), data);
|
const response = await axios.put(prepare(DOCUMENT_UPLOAD_ENDPOINT, { id: document.id as string }), data);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function fileinfo(document: InternshipDocument): Promise<DocumentFileInfo> {
|
||||||
|
const response = await axios.get<DocumentFileInfo>(prepare(DOCUMENT_UPLOAD_ENDPOINT, { id: document.id as string }));
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
46
src/app.tsx
46
src/app.tsx
@ -62,7 +62,7 @@ const LanguageSwitcher = ({ className, ...props }: HTMLProps<HTMLUListElement>)
|
|||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const edition = useSelector<AppState, Edition | null>(state => state.edition);
|
const edition = useSelector<AppState, Edition | null>(state => state.edition as any);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const locale = useSelector<AppState, Locale>(state => getLocale(state.settings));
|
const locale = useSelector<AppState, Locale>(state => getLocale(state.settings));
|
||||||
|
|
||||||
@ -73,27 +73,29 @@ function App() {
|
|||||||
}, [ locale ])
|
}, [ locale ])
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
<header className="header">
|
<header>
|
||||||
<div id="logo" className="header__logo">
|
<Container className="header">
|
||||||
<Link to={ route('home') }>
|
<div id="logo" className="header__logo">
|
||||||
<img src="img/pg-logotyp.svg"/>
|
<Link to={ route('home') }>
|
||||||
</Link>
|
<img src="img/pg-logotyp.svg"/>
|
||||||
</div>
|
</Link>
|
||||||
<div className="header__nav">
|
</div>
|
||||||
<nav className="header__top">
|
<div className="header__nav">
|
||||||
<ul className="header__menu">
|
<nav className="header__top">
|
||||||
</ul>
|
<ul className="header__menu">
|
||||||
<UserMenu className="header__user"/>
|
</ul>
|
||||||
<div className="header__divider"/>
|
<UserMenu className="header__user"/>
|
||||||
<LanguageSwitcher className="header__language-switcher"/>
|
<div className="header__divider"/>
|
||||||
</nav>
|
<LanguageSwitcher className="header__language-switcher"/>
|
||||||
<nav className="header__bottom">
|
</nav>
|
||||||
<ul className="header__menu header__menu--main">
|
<nav className="header__bottom">
|
||||||
<li><Link to={ route("home") }>{ t("pages.my-internship.header") }</Link></li>
|
<ul className="header__menu header__menu--main">
|
||||||
<li><Link to="/regulamin">Regulamin</Link></li>
|
<li><Link to={ route("home") }>{ t("pages.my-internship.header") }</Link></li>
|
||||||
</ul>
|
<li><Link to="/regulamin">Regulamin</Link></li>
|
||||||
</nav>
|
</ul>
|
||||||
</div>
|
</nav>
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
</header>
|
</header>
|
||||||
<main id="content">
|
<main id="content">
|
||||||
{ <Switch>
|
{ <Switch>
|
||||||
|
28
src/components/async.tsx
Normal file
28
src/components/async.tsx
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { AsyncResult } from "@/hooks";
|
||||||
|
import React from "react";
|
||||||
|
import { CircularProgress } from "@material-ui/core";
|
||||||
|
import { Alert } from "@material-ui/lab";
|
||||||
|
|
||||||
|
type AsyncProps<TValue, TError = any> = {
|
||||||
|
async: AsyncResult<TValue>,
|
||||||
|
children: (value: TValue) => JSX.Element,
|
||||||
|
loading?: () => JSX.Element,
|
||||||
|
error?: (error: TError) => JSX.Element,
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultLoading = () => <CircularProgress />;
|
||||||
|
const defaultError = (error: any) => <Alert severity="error">{ error.message }</Alert>;
|
||||||
|
|
||||||
|
export function Async<TValue, TError = any>(
|
||||||
|
{ async, children: render, loading = defaultLoading, error = defaultError }: AsyncProps<TValue, TError>
|
||||||
|
) {
|
||||||
|
if (async.isLoading || (!async.error && !async.value)) {
|
||||||
|
return loading();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof async.error !== "undefined") {
|
||||||
|
return error(async.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return render(async.value as TValue);
|
||||||
|
}
|
93
src/components/fileinfo.tsx
Normal file
93
src/components/fileinfo.tsx
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
import { InternshipDocument } from "@/api/dto/internship-registration";
|
||||||
|
import { useAsync } from "@/hooks";
|
||||||
|
import { DocumentFileInfo } from "@/api/upload";
|
||||||
|
import React, { useCallback } from "react";
|
||||||
|
import api from "@/api";
|
||||||
|
import { Async } from "@/components/async";
|
||||||
|
import { Button, Grid, Paper, PaperProps, SvgIconProps, Theme, Typography } from "@material-ui/core";
|
||||||
|
import { makeStyles, createStyles } from "@material-ui/core/styles";
|
||||||
|
import filesize from "filesize";
|
||||||
|
import { Actions } from "@/components/actions";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { FileDownloadOutline, FileOutline, FileImageOutline, FilePdfOutline, FileWordOutline } from "mdi-material-ui";
|
||||||
|
import classNames from "classnames";
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme: Theme) => createStyles({
|
||||||
|
root: {
|
||||||
|
padding: theme.spacing(2),
|
||||||
|
backgroundColor: "#e9f0f5",
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
color: theme.palette.primary.dark,
|
||||||
|
},
|
||||||
|
download: {
|
||||||
|
color: theme.palette.primary.dark,
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
marginTop: theme.spacing(2),
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
fontSize: "6rem",
|
||||||
|
margin: "0 auto",
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
iconColumn: {
|
||||||
|
flex: "0 1 auto",
|
||||||
|
marginRight: "1rem",
|
||||||
|
color: theme.palette.primary.dark + "af",
|
||||||
|
},
|
||||||
|
asideColumn: {
|
||||||
|
flex: "1 1 auto"
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
export type FileInfoProps = {
|
||||||
|
document: InternshipDocument
|
||||||
|
} & PaperProps;
|
||||||
|
|
||||||
|
export type FileIconProps = {
|
||||||
|
mime: string;
|
||||||
|
} & SvgIconProps;
|
||||||
|
|
||||||
|
export function FileIcon({ mime, ...props }: FileIconProps) {
|
||||||
|
switch (true) {
|
||||||
|
case ["application/pdf", "application/x-pdf"].includes(mime):
|
||||||
|
return <FilePdfOutline {...props} />
|
||||||
|
case mime === "application/vnd.openxmlformats-officedocument.wordprocessingml.document":
|
||||||
|
return <FileWordOutline {...props} />
|
||||||
|
case mime.startsWith("image/"):
|
||||||
|
return <FileImageOutline {...props} />
|
||||||
|
default:
|
||||||
|
return <FileOutline {...props} />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FileInfo = ({ document, ...props }: FileInfoProps) => {
|
||||||
|
const fileinfo = useAsync<DocumentFileInfo>(useCallback(() => api.upload.fileinfo(document), [document.id]));
|
||||||
|
const classes = useStyles();
|
||||||
|
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return <Paper variant="outlined" { ...props } className={ classNames(classes.root, props.className) }>
|
||||||
|
<Async async={ fileinfo }>
|
||||||
|
{ fileinfo => <div className={ classes.grid }>
|
||||||
|
<div className={ classes.iconColumn }>
|
||||||
|
<FileIcon mime={ fileinfo.mime } className={ classes.icon } />
|
||||||
|
</div>
|
||||||
|
<aside className={ classes.asideColumn }>
|
||||||
|
<Typography variant="h5" className={ classes.header }>{ fileinfo.filename }</Typography>
|
||||||
|
<Typography variant="subtitle2">
|
||||||
|
{ filesize(fileinfo.size) } • { fileinfo.mime }
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Actions className={ classes.actions }>
|
||||||
|
<Button className={ classes.download } startIcon={ <FileDownloadOutline /> }>{ t("download") }</Button>
|
||||||
|
</Actions>
|
||||||
|
</aside>
|
||||||
|
</div> }
|
||||||
|
</Async>
|
||||||
|
</Paper>
|
||||||
|
}
|
@ -194,7 +194,7 @@ export const CompanyForm: React.FunctionComponent = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Grid container>
|
<Grid container>
|
||||||
<Grid item>
|
<Grid item xs={12}>
|
||||||
<Autocomplete options={ companies }
|
<Autocomplete options={ companies }
|
||||||
getOptionLabel={ option => typeof option === "string" ? option : option.name }
|
getOptionLabel={ option => typeof option === "string" ? option : option.name }
|
||||||
renderOption={ company => <CompanyItem company={ company }/> }
|
renderOption={ company => <CompanyItem company={ company }/> }
|
||||||
|
@ -141,7 +141,6 @@ const InternshipDurationForm = () => {
|
|||||||
format="DD MMMM yyyy"
|
format="DD MMMM yyyy"
|
||||||
disableToolbar fullWidth
|
disableToolbar fullWidth
|
||||||
variant="inline" label={ t("forms.internship.fields.start-date") }
|
variant="inline" label={ t("forms.internship.fields.start-date") }
|
||||||
minDate={ moment() }
|
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item md={ 6 }>
|
<Grid item md={ 6 }>
|
||||||
@ -149,7 +148,7 @@ const InternshipDurationForm = () => {
|
|||||||
format="DD MMMM yyyy"
|
format="DD MMMM yyyy"
|
||||||
disableToolbar fullWidth
|
disableToolbar fullWidth
|
||||||
variant="inline" label={ t("forms.internship.fields.end-date") }
|
variant="inline" label={ t("forms.internship.fields.end-date") }
|
||||||
minDate={ startDate || moment() }
|
minDate={ startDate }
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item md={ 4 }>
|
<Grid item md={ 4 }>
|
||||||
@ -252,12 +251,8 @@ export const InternshipForm: React.FunctionComponent = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const edition = useCurrentEdition();
|
const edition = useCurrentEdition();
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
const history = useHistory();
|
|
||||||
|
|
||||||
const [confirmDialogOpen, setConfirmDialogOpen] = useState<boolean>(false);
|
const [confirmDialogOpen, setConfirmDialogOpen] = useState<boolean>(false);
|
||||||
|
|
||||||
const validationSchema = Yup.object<Partial<InternshipFormValues>>({
|
const validationSchema = Yup.object<Partial<InternshipFormValues>>({
|
||||||
|
@ -41,19 +41,19 @@ export const PlanForm = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return <Grid container>
|
return <Grid container>
|
||||||
<Grid item>
|
<Grid item xs={12}>
|
||||||
<Typography variant="body1" component="p">{ t('forms.plan.instructions') }</Typography>
|
<Typography variant="body1" component="p">{ t('forms.plan.instructions') }</Typography>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item>
|
<Grid item xs={12}>
|
||||||
<Button href="https://eti.pg.edu.pl/documents/611675/100028367/indywidualny%20program%20praktyk" startIcon={ <DescriptionIcon /> }>
|
<Button href="https://eti.pg.edu.pl/documents/611675/100028367/indywidualny%20program%20praktyk" startIcon={ <DescriptionIcon /> }>
|
||||||
{ t('steps.plan.template') }
|
{ t('steps.plan.template') }
|
||||||
</Button>
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item>
|
<Grid item xs={12}>
|
||||||
<DropzoneArea acceptedFiles={["image/*", "application/pdf"]} filesLimit={ 1 } dropzoneText={ t("dropzone") } onChange={ files => setFile(files[0]) }/>
|
<DropzoneArea acceptedFiles={["image/*", "application/pdf"]} filesLimit={ 1 } dropzoneText={ t("dropzone") } onChange={ files => setFile(files[0]) }/>
|
||||||
<FormHelperText>{ t('forms.plan.dropzone-help') }</FormHelperText>
|
<FormHelperText>{ t('forms.plan.dropzone-help') }</FormHelperText>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item>
|
<Grid item xs={12}>
|
||||||
<Actions>
|
<Actions>
|
||||||
<Button variant="contained" color="primary" onClick={ handleSubmit }>
|
<Button variant="contained" color="primary" onClick={ handleSubmit }>
|
||||||
{ t('confirm') }
|
{ t('confirm') }
|
||||||
|
@ -35,7 +35,7 @@ export const StudentForm = () => {
|
|||||||
<Grid item md={3}>
|
<Grid item md={3}>
|
||||||
<TextField label={ t("forms.internship.fields.semester") } value={ student.semester || "" } disabled fullWidth/>
|
<TextField label={ t("forms.internship.fields.semester") } value={ student.semester || "" } disabled fullWidth/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item>
|
<Grid item xs={12}>
|
||||||
<Alert severity="warning" action={ <Button color="inherit" size="small">skontaktuj się z opiekunem</Button> }>
|
<Alert severity="warning" action={ <Button color="inherit" size="small">skontaktuj się z opiekunem</Button> }>
|
||||||
Powyższe dane nie są poprawne?
|
Powyższe dane nie są poprawne?
|
||||||
</Alert>
|
</Alert>
|
||||||
|
@ -10,6 +10,8 @@ import { Actions } from "@/components";
|
|||||||
import { Nullable } from "@/helpers";
|
import { Nullable } from "@/helpers";
|
||||||
import * as Yup from "yup";
|
import * as Yup from "yup";
|
||||||
import { StudentActions, useDispatch } from "@/state/actions";
|
import { StudentActions, useDispatch } from "@/state/actions";
|
||||||
|
import { route } from "@/routing";
|
||||||
|
import { useHistory } from "react-router-dom";
|
||||||
|
|
||||||
interface StudentFormValues {
|
interface StudentFormValues {
|
||||||
firstName: string;
|
firstName: string;
|
||||||
@ -48,6 +50,7 @@ const studentToFormValuesTransformer: Transformer<Nullable<Student>, StudentForm
|
|||||||
export const StudentForm = ({ student }: StudentFormProps) => {
|
export const StudentForm = ({ student }: StudentFormProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
const history = useHistory();
|
||||||
|
|
||||||
const validationSchema = useMemo(() => Yup.object<StudentFormValues>({
|
const validationSchema = useMemo(() => Yup.object<StudentFormValues>({
|
||||||
semester: Yup.number().required().min(1).max(10),
|
semester: Yup.number().required().min(1).max(10),
|
||||||
@ -71,6 +74,8 @@ export const StudentForm = ({ student }: StudentFormProps) => {
|
|||||||
type: StudentActions.Set,
|
type: StudentActions.Set,
|
||||||
student: updated,
|
student: updated,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
history.push(route("home"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -36,6 +36,10 @@ export const updateInternshipInfo = async (dispatch: AppDispatch) => {
|
|||||||
document: plan,
|
document: plan,
|
||||||
state: plan.state,
|
state: plan.state,
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
dispatch({
|
||||||
|
type: InternshipPlanActions.Reset,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,13 +12,14 @@ import { Alert, AlertTitle } from "@material-ui/lab";
|
|||||||
import { ContactAction, Status } from "@/pages/steps/common";
|
import { ContactAction, Status } from "@/pages/steps/common";
|
||||||
import { Description as DescriptionIcon } from "@material-ui/icons";
|
import { Description as DescriptionIcon } from "@material-ui/icons";
|
||||||
import { useDeadlines } from "@/hooks";
|
import { useDeadlines } from "@/hooks";
|
||||||
|
import { InternshipDocument } from "@/api/dto/internship-registration";
|
||||||
|
import { FileInfo } from "@/components/fileinfo";
|
||||||
|
import { useSpacing } from "@/styles";
|
||||||
|
|
||||||
const PlanActions = () => {
|
const PlanActions = () => {
|
||||||
const status = useSelector<AppState, SubmissionStatus>(state => getSubmissionStatus(state.plan));
|
const status = useSelector<AppState, SubmissionStatus>(state => getSubmissionStatus(state.plan));
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
const ReviewAction = (props: ButtonProps) =>
|
const { t } = useTranslation();
|
||||||
<Button startIcon={ <FileDownloadOutline /> } color="primary" { ...props }>{ t('steps.plan.download') }</Button>
|
|
||||||
|
|
||||||
const FormAction = ({ children = t('steps.plan.form'), ...props }: ButtonProps) =>
|
const FormAction = ({ children = t('steps.plan.form'), ...props }: ButtonProps) =>
|
||||||
<Button to={ route("internship_plan") } variant="contained" color="primary" component={ RouterLink } startIcon={ <FileUploadOutline /> }>
|
<Button to={ route("internship_plan") } variant="contained" color="primary" component={ RouterLink } startIcon={ <FileUploadOutline /> }>
|
||||||
@ -32,18 +33,14 @@ const PlanActions = () => {
|
|||||||
|
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case "awaiting":
|
case "awaiting":
|
||||||
return <Actions>
|
return <Actions/>
|
||||||
<ReviewAction />
|
|
||||||
</Actions>
|
|
||||||
case "accepted":
|
case "accepted":
|
||||||
return <Actions>
|
return <Actions>
|
||||||
<ReviewAction/>
|
|
||||||
<FormAction variant="outlined" color="secondary">{ t('send-again') }</FormAction>
|
<FormAction variant="outlined" color="secondary">{ t('send-again') }</FormAction>
|
||||||
</Actions>
|
</Actions>
|
||||||
case "declined":
|
case "declined":
|
||||||
return <Actions>
|
return <Actions>
|
||||||
<FormAction>{ t('fix-errors') }</FormAction>
|
<FormAction>{ t('fix-errors') }</FormAction>
|
||||||
<ReviewAction />
|
|
||||||
<TemplateAction />
|
<TemplateAction />
|
||||||
<ContactAction/>
|
<ContactAction/>
|
||||||
</Actions>
|
</Actions>
|
||||||
@ -72,6 +69,8 @@ export const PlanStep = (props: StepProps) => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const submission = useSelector<AppState, SubmissionState>(state => state.plan);
|
const submission = useSelector<AppState, SubmissionState>(state => state.plan);
|
||||||
|
const document = useSelector<AppState, InternshipDocument>(state => state.plan.document as InternshipDocument);
|
||||||
|
const spacing = useSpacing(2);
|
||||||
|
|
||||||
const status = getSubmissionStatus(submission);
|
const status = getSubmissionStatus(submission);
|
||||||
const deadlines = useDeadlines();
|
const deadlines = useDeadlines();
|
||||||
@ -83,10 +82,13 @@ export const PlanStep = (props: StepProps) => {
|
|||||||
active={ true } completed={ sent } declined={ declined } waiting={ status == "awaiting" }
|
active={ true } completed={ sent } declined={ declined } waiting={ status == "awaiting" }
|
||||||
until={ deadlines.proposal }
|
until={ deadlines.proposal }
|
||||||
state={ <Status submission={ submission } /> }>
|
state={ <Status submission={ submission } /> }>
|
||||||
<p>{ t(`steps.plan.info.${ status }`) }</p>
|
<div className={ spacing.vertical }>
|
||||||
|
<p>{ t(`steps.plan.info.${ status }`) }</p>
|
||||||
|
|
||||||
{ comment && <Box pb={ 2 }><PlanComment/></Box> }
|
{ comment && <Box pb={ 2 }><PlanComment/></Box> }
|
||||||
|
{ document && <FileInfo document={ document } /> }
|
||||||
|
|
||||||
<PlanActions/>
|
<PlanActions/>
|
||||||
|
</div>
|
||||||
</Step>;
|
</Step>;
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ export const StudentStep = (props: StepProps) => {
|
|||||||
</> : <>
|
</> : <>
|
||||||
<p>{ t('steps.personal-data.all-filled') }</p>
|
<p>{ t('steps.personal-data.all-filled') }</p>
|
||||||
<Actions>
|
<Actions>
|
||||||
<Button to={ route("user_profile") } variant="outlined" color="primary" component={ RouterLink } startIcon={ <AccountDetails /> }>
|
<Button to={ route("user_profile") } component={ RouterLink } startIcon={ <AccountDetails /> }>
|
||||||
{ t('steps.personal-data.actions.info') }
|
{ t('steps.personal-data.actions.info') }
|
||||||
</Button>
|
</Button>
|
||||||
</Actions>
|
</Actions>
|
||||||
|
@ -7,6 +7,7 @@ import {
|
|||||||
} from "@/state/actions/submission";
|
} from "@/state/actions/submission";
|
||||||
|
|
||||||
import { InternshipDocument, SubmissionState } from "@/api/dto/internship-registration";
|
import { InternshipDocument, SubmissionState } from "@/api/dto/internship-registration";
|
||||||
|
import { Action } from "@/state/actions/base";
|
||||||
|
|
||||||
export enum InternshipPlanActions {
|
export enum InternshipPlanActions {
|
||||||
Send = "SEND_PLAN",
|
Send = "SEND_PLAN",
|
||||||
@ -14,8 +15,11 @@ export enum InternshipPlanActions {
|
|||||||
Approve = "RECEIVE_PLAN_APPROVE",
|
Approve = "RECEIVE_PLAN_APPROVE",
|
||||||
Decline = "RECEIVE_PLAN_DECLINE",
|
Decline = "RECEIVE_PLAN_DECLINE",
|
||||||
Receive = "RECEIVE_PLAN_STATE",
|
Receive = "RECEIVE_PLAN_STATE",
|
||||||
|
Reset = "RESET_PLAN",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ResetPlanAction extends Action<InternshipPlanActions.Reset> {}
|
||||||
|
|
||||||
export interface SendPlanAction extends SendSubmissionAction<InternshipPlanActions.Send> {
|
export interface SendPlanAction extends SendSubmissionAction<InternshipPlanActions.Send> {
|
||||||
document: InternshipDocument;
|
document: InternshipDocument;
|
||||||
}
|
}
|
||||||
@ -40,4 +44,6 @@ export type InternshipPlanAction
|
|||||||
| SavePlanAction
|
| SavePlanAction
|
||||||
| ReceivePlanApproveAction
|
| ReceivePlanApproveAction
|
||||||
| ReceivePlanDeclineAction
|
| ReceivePlanDeclineAction
|
||||||
| ReceivePlanUpdateAction;
|
| ReceivePlanUpdateAction
|
||||||
|
| ResetPlanAction
|
||||||
|
;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { InternshipPlanAction, InternshipPlanActions, InternshipProposalActions } from "@/state/actions";
|
import { InternshipPlanAction, InternshipPlanActions } from "@/state/actions";
|
||||||
import { Serializable } from "@/serialization/types";
|
import { Serializable } from "@/serialization/types";
|
||||||
import {
|
import {
|
||||||
createSubmissionReducer,
|
createSubmissionReducer,
|
||||||
@ -51,6 +51,9 @@ const internshipPlanReducer = (state: InternshipPlanState = defaultInternshipPla
|
|||||||
document: action.document,
|
document: action.document,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case InternshipPlanActions.Reset:
|
||||||
|
return defaultInternshipPlanState;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
@import "variables";
|
@import "variables";
|
||||||
|
|
||||||
.header {
|
header {
|
||||||
height: 110px;
|
|
||||||
background: $main;
|
background: $main;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
height: 110px;
|
||||||
|
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
@ -17,3 +17,16 @@ export const useHorizontalSpacing = makeStyles(theme => createStyles({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
export const useSpacing = makeStyles(theme => createStyles({
|
||||||
|
horizontal: {
|
||||||
|
"& > *:not(:last-child)": {
|
||||||
|
marginRight: (spacing: number = defaultSpacing) => theme.spacing(spacing)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
vertical: {
|
||||||
|
"& > *:not(:last-child)": {
|
||||||
|
marginBottom: (spacing: number = defaultSpacing) => theme.spacing(spacing)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
@ -4,7 +4,6 @@ export const studentTheme = responsiveFontSizes(createMuiTheme({
|
|||||||
props: {
|
props: {
|
||||||
MuiGrid: {
|
MuiGrid: {
|
||||||
spacing: 3,
|
spacing: 3,
|
||||||
xs: 12,
|
|
||||||
},
|
},
|
||||||
MuiContainer: {
|
MuiContainer: {
|
||||||
maxWidth: "md"
|
maxWidth: "md"
|
||||||
|
@ -200,3 +200,4 @@ validation:
|
|||||||
minimum-hours: "Minimalna liczba godzin wynosi {{ hours }}"
|
minimum-hours: "Minimalna liczba godzin wynosi {{ hours }}"
|
||||||
|
|
||||||
contact-coordinator: "Skontaktuj się z koordynatorem"
|
contact-coordinator: "Skontaktuj się z koordynatorem"
|
||||||
|
download: "pobierz"
|
||||||
|
@ -3858,6 +3858,11 @@ filesize@6.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/filesize/-/filesize-6.0.1.tgz#f850b509909c7c86f7e450ea19006c31c2ed3d2f"
|
resolved "https://registry.yarnpkg.com/filesize/-/filesize-6.0.1.tgz#f850b509909c7c86f7e450ea19006c31c2ed3d2f"
|
||||||
integrity sha512-u4AYWPgbI5GBhs6id1KdImZWn5yfyFrrQ8OWZdN7ZMfA8Bf4HcO0BGo9bmUIEV8yrp8I1xVfJ/dn90GtFNNJcg==
|
integrity sha512-u4AYWPgbI5GBhs6id1KdImZWn5yfyFrrQ8OWZdN7ZMfA8Bf4HcO0BGo9bmUIEV8yrp8I1xVfJ/dn90GtFNNJcg==
|
||||||
|
|
||||||
|
filesize@^6.1.0:
|
||||||
|
version "6.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/filesize/-/filesize-6.1.0.tgz#e81bdaa780e2451d714d71c0d7a4f3238d37ad00"
|
||||||
|
integrity sha512-LpCHtPQ3sFx67z+uh2HnSyWSLLu5Jxo21795uRDuar/EOuYWXib5EmPaGIBuSnRqH2IODiKA2k5re/K9OnN/Yg==
|
||||||
|
|
||||||
fill-range@^4.0.0:
|
fill-range@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7"
|
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7"
|
||||||
|
Loading…
Reference in New Issue
Block a user