Add ability to accept or decline ipp

This commit is contained in:
Kacper Donat 2020-11-11 16:35:36 +01:00
parent c13d880baa
commit 9d762f4ed2
5 changed files with 162 additions and 103 deletions

View File

@ -1,15 +1,16 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta name="theme-color" content="#000000" /> <meta name="theme-color" content="#000000"/>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&amp;display=swap&amp;subset=latin,latin-ext" /> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&amp;display=swap&amp;subset=latin,latin-ext"/>
<title>Zgłoszenie praktyki studenckiej</title> <title>Zgłoszenie praktyki studenckiej</title>
<base href="/"> <base href="/">
</head> </head>
<body> <body>
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div> <div id="root"></div>
</body> <div id="modals"></div>
</body>
</html> </html>

View File

@ -0,0 +1,115 @@
import React, { useState } from "react";
import { Button, ButtonGroup, Dialog, DialogActions, DialogContent, DialogTitle, Menu, MenuItem, TextField, Typography } from "@material-ui/core";
import { MenuDown, StickerCheckOutline, StickerRemoveOutline } from "mdi-material-ui";
import { useTranslation } from "react-i18next";
import { useVerticalSpacing } from "@/styles";
import { createPortal } from "react-dom";
type AcceptanceActionsProps = {
onAccept: (comment?: string) => void;
onDiscard: (comment: string) => void;
label: string;
}
export function AcceptanceActions({ onAccept, onDiscard, label }: AcceptanceActionsProps) {
const { t } = useTranslation();
const [isDiscardModalOpen, setDiscardModelOpen] = useState<boolean>(false);
const [isAcceptModalOpen, setAcceptModelOpen] = useState<boolean>(false);
const [comment, setComment] = useState<string>("");
const [menuAnchor, setMenuAnchor] = useState<null | HTMLElement>(null);
const classes = useVerticalSpacing(3);
const handleAccept = () => {
onAccept(comment);
}
const handleDiscard = () => {
onDiscard(comment);
}
const handleAcceptModalClose = () => {
setAcceptModelOpen(false);
}
const handleDiscardModalClose = () => {
setDiscardModelOpen(false);
}
const handleDiscardAction = () => {
setDiscardModelOpen(true);
}
const handleAcceptMenuOpen = (ev: React.MouseEvent<HTMLElement>) => {
setMenuAnchor(ev.currentTarget);
}
const handleAcceptMenuClose = () => {
setMenuAnchor(null);
}
const handleAcceptWithComment = () => {
setAcceptModelOpen(true);
setMenuAnchor(null);
}
const handleAcceptWithoutComment = () => {
onAccept();
}
return <>
<ButtonGroup color="primary" variant="contained">
<Button onClick={ handleAcceptWithoutComment } startIcon={ <StickerCheckOutline /> }>
{ t('accept-without-comments') }
</Button>
<Button size="small" onClick={ handleAcceptMenuOpen }><MenuDown /></Button>
</ButtonGroup>
<Menu open={ !!menuAnchor } anchorEl={ menuAnchor } onClose={ handleAcceptMenuClose }>
<MenuItem onClick={ handleAcceptWithoutComment }>{ t("accept-without-comments") }</MenuItem>
<MenuItem onClick={ handleAcceptWithComment }>{ t("accept-with-comments") }</MenuItem>
</Menu>
<Button onClick={ handleDiscardAction } color="secondary" startIcon={ <StickerRemoveOutline /> }>
{ t('discard') }
</Button>
{ createPortal(<>
<Dialog open={ isDiscardModalOpen } onClose={ handleDiscardModalClose } maxWidth="md">
<DialogTitle>{ t(label + ".discard.title") }</DialogTitle>
<DialogContent className={ classes.root }>
<Typography variant="body1">{ t(label + ".discard.info") }</Typography>
<TextField multiline value={ comment } onChange={ ev => setComment(ev.target.value) } fullWidth label={ t("comments") } rows={3}/>
<DialogActions>
<Button onClick={ handleDiscardModalClose }>
{ t('cancel') }
</Button>
<Button onClick={ handleDiscard } color="primary" variant="contained">
{ t('confirm') }
</Button>
</DialogActions>
</DialogContent>
</Dialog>
<Dialog open={ isAcceptModalOpen } onClose={ handleAcceptModalClose } maxWidth="md">
<DialogTitle>{ t(label + ".accept.title") }</DialogTitle>
<DialogContent className={ classes.root }>
<Typography variant="body1">{ t(label + ".accept.info") }</Typography>
<TextField multiline value={ comment } onChange={ ev => setComment(ev.target.value) } fullWidth label={ t("comments") } rows={3}/>
<DialogActions>
<Button onClick={ handleAcceptModalClose }>
{ t('cancel') }
</Button>
<Button onClick={ handleAccept } color="primary" variant="contained">
{ t('confirm') }
</Button>
</DialogActions>
</DialogContent>
</Dialog>
</>, document.getElementById("modals") as Element) }
</>
}

View File

@ -28,6 +28,7 @@ import { Actions } from "@/components";
import { InternshipProposalActions, useDispatch } from "@/state/actions"; import { InternshipProposalActions, useDispatch } from "@/state/actions";
import { MenuDown, StickerCheckOutline, StickerRemoveOutline } from "mdi-material-ui/index"; import { MenuDown, StickerCheckOutline, StickerRemoveOutline } from "mdi-material-ui/index";
import { useVerticalSpacing } from "@/styles"; import { useVerticalSpacing } from "@/styles";
import { AcceptanceActions } from "@/components/acceptance-action";
export const InternshipProposalFormPage = () => { export const InternshipProposalFormPage = () => {
const { t } = useTranslation(); const { t } = useTranslation();
@ -54,49 +55,13 @@ export const InternshipProposalPreviewPage = () => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const history = useHistory(); const history = useHistory();
const [isDiscardModalOpen, setDiscardModelOpen] = useState<boolean>(false); const handleAccept = (comment?: string) => {
const [isAcceptModalOpen, setAcceptModelOpen] = useState<boolean>(false); dispatch({ type: InternshipProposalActions.Approve, comment: comment || null });
const [comment, setComment] = useState<string>("");
const [menuAnchor, setMenuAnchor] = useState<null | HTMLElement>(null);
const handleAccept = () => {
dispatch({ type: InternshipProposalActions.Approve, comment });
history.push(route("home")); history.push(route("home"));
} }
const handleDiscard = () => { const handleDiscard = (comment: string) => {
dispatch({ type: InternshipProposalActions.Decline, comment }); dispatch({ type: InternshipProposalActions.Decline, comment: comment });
history.push(route("home"));
}
const handleAcceptModalClose = () => {
setAcceptModelOpen(false);
}
const handleDiscardModalClose = () => {
setDiscardModelOpen(false);
}
const handleDiscardAction = () => {
setDiscardModelOpen(true);
}
const handleAcceptMenuOpen = (ev: React.MouseEvent<HTMLElement>) => {
setMenuAnchor(ev.currentTarget);
}
const handleAcceptMenuClose = () => {
setMenuAnchor(null);
}
const handleAcceptWithComment = () => {
setAcceptModelOpen(true);
setMenuAnchor(null);
}
const handleAcceptWithoutComment = () => {
dispatch({ type: InternshipProposalActions.Approve, comment: null });
history.push(route("home")); history.push(route("home"));
} }
@ -115,59 +80,13 @@ export const InternshipProposalPreviewPage = () => {
{ proposal && <ProposalPreview proposal={ proposal } /> } { proposal && <ProposalPreview proposal={ proposal } /> }
<Actions> <Actions>
<ButtonGroup color="primary" variant="contained"> <AcceptanceActions onAccept={ handleAccept } onDiscard={ handleDiscard } label="internship" />
<Button onClick={ handleAcceptWithoutComment } startIcon={ <StickerCheckOutline /> }>
{ t('accept-without-comments') }
</Button>
<Button size="small" onClick={ handleAcceptMenuOpen }><MenuDown /></Button>
</ButtonGroup>
<Menu open={ !!menuAnchor } anchorEl={ menuAnchor } onClose={ handleAcceptMenuClose }>
<MenuItem onClick={ handleAcceptWithoutComment }>{ t("accept-without-comments") }</MenuItem>
<MenuItem onClick={ handleAcceptWithComment }>{ t("accept-with-comments") }</MenuItem>
</Menu>
<Button onClick={ handleDiscardAction } color="secondary" startIcon={ <StickerRemoveOutline /> }>
{ t('discard') }
</Button>
<Button component={ RouterLink } to={ route("home") }> <Button component={ RouterLink } to={ route("home") }>
{ t('go-back') } { t('go-back') }
</Button> </Button>
</Actions> </Actions>
</Container> </Container>
<Dialog open={ isDiscardModalOpen } onClose={ handleDiscardModalClose } maxWidth="md">
<DialogTitle>{ t("internship.discard.title") }</DialogTitle>
<DialogContent className={ classes.root }>
<Typography variant="body1">{ t("internship.discard.info") }</Typography>
<TextField multiline value={ comment } onChange={ ev => setComment(ev.target.value) } fullWidth label={ t("comments") } rows={3}/>
<DialogActions>
<Button onClick={ handleDiscardModalClose }>
{ t('cancel') }
</Button>
<Button onClick={ handleDiscard } color="primary" variant="contained">
{ t('confirm') }
</Button>
</DialogActions>
</DialogContent>
</Dialog>
<Dialog open={ isAcceptModalOpen } onClose={ handleAcceptModalClose } maxWidth="md">
<DialogTitle>{ t("internship.accept.title") }</DialogTitle>
<DialogContent className={ classes.root }>
<Typography variant="body1">{ t("internship.accept.info") }</Typography>
<TextField multiline value={ comment } onChange={ ev => setComment(ev.target.value) } fullWidth label={ t("comments") } rows={3}/>
<DialogActions>
<Button onClick={ handleAcceptModalClose }>
{ t('cancel') }
</Button>
<Button onClick={ handleAccept } color="primary" variant="contained">
{ t('confirm') }
</Button>
</DialogActions>
</DialogContent>
</Dialog>
</Page> </Page>
} }

View File

@ -5,7 +5,7 @@ import { useTranslation } from "react-i18next";
import { Box, Button, ButtonProps, StepProps } from "@material-ui/core"; import { Box, Button, ButtonProps, StepProps } from "@material-ui/core";
import { FileDownloadOutline, FileUploadOutline } from "mdi-material-ui/index"; import { FileDownloadOutline, FileUploadOutline } from "mdi-material-ui/index";
import { route } from "@/routing"; import { route } from "@/routing";
import { Link as RouterLink } from "react-router-dom"; import { Link as RouterLink, useHistory } from "react-router-dom";
import { Actions, Step } from "@/components"; import { Actions, Step } from "@/components";
import React, { HTMLProps } from "react"; import React, { HTMLProps } from "react";
import { Alert, AlertTitle } from "@material-ui/lab"; import { Alert, AlertTitle } from "@material-ui/lab";
@ -15,15 +15,19 @@ import { useDeadlines } from "@/hooks";
import { InternshipDocument } from "@/api/dto/internship-registration"; import { InternshipDocument } from "@/api/dto/internship-registration";
import { FileInfo } from "@/components/fileinfo"; import { FileInfo } from "@/components/fileinfo";
import { useSpacing } from "@/styles"; import { useSpacing } from "@/styles";
import { AcceptanceActions } from "@/components/acceptance-action";
import { InternshipPlanActions, useDispatch } from "@/state/actions";
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 { t } = useTranslation();
const dispatch = useDispatch();
const history = useHistory();
const FormAction = ({ children = t('steps.plan.form'), ...props }: ButtonProps) => const FormAction = ({ children = t('steps.plan.submit'), ...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 /> }>
{ t('steps.plan.submit') } { children }
</Button> </Button>
const TemplateAction = (props: ButtonProps) => const TemplateAction = (props: ButtonProps) =>
@ -31,9 +35,21 @@ const PlanActions = () => {
{ t('steps.plan.template') } { t('steps.plan.template') }
</Button> </Button>
const handleAccept = (comment?: string) => {
dispatch({ type: InternshipPlanActions.Approve, comment: comment || null });
history.push(route("home"));
}
const handleDiscard = (comment: string) => {
dispatch({ type: InternshipPlanActions.Decline, comment: comment });
history.push(route("home"));
}
switch (status) { switch (status) {
case "awaiting": case "awaiting":
return <Actions/> return <Actions>
<AcceptanceActions onAccept={ handleAccept } onDiscard={ handleDiscard } label="plan" />
</Actions>
case "accepted": case "accepted":
return <Actions> return <Actions>
<FormAction variant="outlined" color="secondary">{ t('send-again') }</FormAction> <FormAction variant="outlined" color="secondary">{ t('send-again') }</FormAction>

View File

@ -148,6 +148,14 @@ internship:
title: "Zaakceptuj zgłoszenie praktyki" title: "Zaakceptuj zgłoszenie praktyki"
info: "Poniższa informacja zostanie przekazana praktykantowi." info: "Poniższa informacja zostanie przekazana praktykantowi."
plan:
discard:
title: "Odrzuć indywidualny program praktyki"
info: "Poniższa informacja zostanie przekazana praktykantowi w celu poprawy zgłoszenia."
accept:
title: "Zaakceptuj indywidualny program praktyki"
info: "Poniższa informacja zostanie przekazana praktykantowi."
steps: steps:
personal-data: personal-data:
header: "Uzupełnienie danych" header: "Uzupełnienie danych"