diff --git a/src/components/contact.tsx b/src/components/contact.tsx new file mode 100644 index 0000000..03b5cea --- /dev/null +++ b/src/components/contact.tsx @@ -0,0 +1,74 @@ +import React, { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { useSpacing } from "@/styles"; +import { Field, Form, Formik } from "formik"; +import { Button, Dialog, DialogActions, DialogContent, DialogProps, DialogTitle, Typography } from "@material-ui/core"; +import { CKEditorField } from "@/field/ckeditor"; +import { Actions } from "@/components/actions"; +import { Cancel, Send } from "mdi-material-ui"; +import { createPortal } from "react-dom"; +import { capitalize } from "@/helpers"; + +export type ContactFormValues = { + content: string; +} + +const initialContactFormValues: ContactFormValues = { + content: "", +} + +export type ContactDialogProps = { + onSend: (values: ContactFormValues) => void; +} & DialogProps; + +export function ContactForm() { + const { t } = useTranslation(); + const spacing = useSpacing(2); + + return
+ +
+} + +export function ContactDialog({ onSend, ...props }: ContactDialogProps) { + const spacing = useSpacing(2); + const { t } = useTranslation(); + + return + +
+ { capitalize(t("forms.contact.title")) } + + + + + + + + + +
+
+
+} + +export type ContactActionProps = { + children: (props: { action: () => void }) => React.ReactNode +}; + +export function ContactAction({ children }: ContactActionProps) { + const [open, setOpen] = useState(false); + + const handleClose = () => { setOpen(false) }; + const handleSubmit = (values: ContactFormValues) => { + setOpen(false); + } + + return <> + { children({ action: () => setOpen(true) }) } + { createPortal( + , + document.getElementById("modals") as HTMLElement + ) } + +} diff --git a/src/forms/internship.tsx b/src/forms/internship.tsx index c5d83f0..e8dd4c9 100644 --- a/src/forms/internship.tsx +++ b/src/forms/internship.tsx @@ -112,7 +112,7 @@ const InternshipProgramForm = () => { if (ev.target.checked) { setSelectedProgramEntries([ ...selectedProgramEntries, entry ]); } else { - setSelectedProgramEntries(selectedProgramEntries.filter(cur => cur != entry)); + setSelectedProgramEntries(selectedProgramEntries.filter(cur => cur.id != entry.id)); } } @@ -133,6 +133,7 @@ const InternshipProgramForm = () => { onBlur={ handleBlur } /> + { values.kind?.requiresDeanApproval && { t("internship.kind-requires-dean-approval") } } {/**/} {/* {*/} {/* values.kind === InternshipType.Other &&*/} @@ -159,6 +160,7 @@ const InternshipProgramForm = () => { const InternshipDurationForm = () => { const { t } = useTranslation(); + const edition = useCurrentEdition(); const { values: { startDate, endDate, workingHours }, errors, @@ -174,6 +176,8 @@ const InternshipDurationForm = () => { const hours = useMemo(() => overrideHours !== null ? overrideHours : computedHours || null, [overrideHours, computedHours]); const weeks = useMemo(() => hours !== null ? Math.floor(hours / workingHours) : null, [ hours ]); + const requiresDeanApproval = useMemo(() => edition?.startDate?.isAfter(startDate) || edition?.endDate?.isBefore(endDate), [ startDate, endDate ]) + useUpdateEffect(() => { setFieldTouched("hours", true); setFieldValue("hours", hours, true); @@ -200,6 +204,9 @@ const InternshipDurationForm = () => { minDate={ startDate } /> + { requiresDeanApproval && + { t("internship.duration-requires-dean-approval") } + } { if (!destination) { destination = await api.upload.create(UploadType.Ipp); - dispatch({ type: InternshipPlanActions.Send, document: destination }); } + dispatch({ type: InternshipPlanActions.Send, document: destination }); + await api.upload.upload(destination, file); history.push("/"); diff --git a/src/forms/student.tsx b/src/forms/student.tsx index 3d3cead..422e9fc 100644 --- a/src/forms/student.tsx +++ b/src/forms/student.tsx @@ -6,6 +6,7 @@ import { useTranslation } from "react-i18next"; import { useFormikContext } from "formik"; import { InternshipFormValues } from "@/forms/internship"; import { useCurrentEdition } from "@/hooks"; +import { ContactAction } from "@/components/contact"; export const StudentForm = () => { const { t } = useTranslation(); @@ -36,8 +37,10 @@ export const StudentForm = () => { - skontaktuj się z opiekunem }> - Powyższe dane nie są poprawne? + { + ({ action }) => + } }> + { t("incorrect-data-question") } diff --git a/src/helpers.ts b/src/helpers.ts index 1d66767..cfb9ee9 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -43,3 +43,7 @@ export function one(value: OneOrMany): T { return value; } + +export function capitalize(value: string): string { + return value.charAt(0).toUpperCase() + value.slice(1); +} diff --git a/src/pages/steps/common.tsx b/src/pages/steps/common.tsx index 58fdb8e..6a7d61f 100644 --- a/src/pages/steps/common.tsx +++ b/src/pages/steps/common.tsx @@ -4,6 +4,7 @@ import { createStyles, makeStyles } from "@material-ui/core/styles"; import { useTranslation } from "react-i18next"; import React from "react"; import { CommentQuestion } from "mdi-material-ui/index"; +import { ContactAction } from "@/components/contact"; export const getColorByStatus = (status: SubmissionStatus, theme: Theme) => { switch (status) { @@ -46,8 +47,12 @@ export const Status = ({ submission } : SubmissionStatusProps) => { return { t(`submission.status.${ status }`) }; } -export const ContactAction = (props: ButtonProps) => { +export const ContactButton = (props: ButtonProps) => { const { t } = useTranslation(); - return + return { + ({ action }) => } + } diff --git a/src/pages/steps/insurance.tsx b/src/pages/steps/insurance.tsx index b005389..252902d 100644 --- a/src/pages/steps/insurance.tsx +++ b/src/pages/steps/insurance.tsx @@ -4,7 +4,7 @@ import { InsuranceState } from "@/state/reducer/insurance"; import { Actions, Step } from "@/components"; import { useTranslation } from "react-i18next"; import React from "react"; -import { ContactAction } from "@/pages/steps/common"; +import { ContactButton } from "@/pages/steps/common"; import { useDeadlines } from "@/hooks"; import { StepProps } from "@material-ui/core"; @@ -17,7 +17,7 @@ export const InsuranceStep = (props: StepProps) => { return

{ t(`steps.insurance.instructions`) }

- +
} diff --git a/src/pages/steps/plan.tsx b/src/pages/steps/plan.tsx index 2e15ec3..7d4d523 100644 --- a/src/pages/steps/plan.tsx +++ b/src/pages/steps/plan.tsx @@ -9,7 +9,7 @@ import { Link as RouterLink, useHistory } from "react-router-dom"; import { Actions, Step } from "@/components"; import React, { HTMLProps } from "react"; import { Alert, AlertTitle } from "@material-ui/lab"; -import { ContactAction, Status } from "@/pages/steps/common"; +import { ContactButton, Status } from "@/pages/steps/common"; import { Description as DescriptionIcon } from "@material-ui/icons"; import { useDeadlines } from "@/hooks"; import { InternshipDocument } from "@/api/dto/internship-registration"; @@ -56,9 +56,9 @@ const PlanActions = () => { case "declined": return - { t('fix-errors') } + { t('send-again') } - + case "draft": return diff --git a/src/pages/steps/proposal.tsx b/src/pages/steps/proposal.tsx index 9996ba4..697e904 100644 --- a/src/pages/steps/proposal.tsx +++ b/src/pages/steps/proposal.tsx @@ -10,7 +10,7 @@ import { Actions, Step } from "@/components"; import { route } from "@/routing"; import { Link as RouterLink } from "react-router-dom"; import { ClipboardEditOutline, FileFind } from "mdi-material-ui/index"; -import { ContactAction, Status } from "@/pages/steps/common"; +import { ContactButton, Status } from "@/pages/steps/common"; import { useDeadlines } from "@/hooks"; const ProposalActions = () => { @@ -43,7 +43,7 @@ const ProposalActions = () => { case "declined": return { t('fix-errors') } - + case "draft": return diff --git a/translations/pl.yaml b/translations/pl.yaml index 294a9ff..d5d2c18 100644 --- a/translations/pl.yaml +++ b/translations/pl.yaml @@ -26,12 +26,15 @@ contact: skontaktuj się z pełnomocnikiem comments: Zgłoszone uwagi send-again: wyślij ponownie cancel: anuluj +send: wyślij accept: zaakceptuj accept-with-comments: zaakceptuj z uwagami accept-without-comments: zaakceptuj bez uwag discard: zgłoś uwagi +incorrect-data-question: "Powyższe dane nie są poprawne?" + dropzone: "Przeciągnij i upuść plik bądź kliknij, aby wybrać" pages: @@ -63,6 +66,10 @@ forms: sections: personal: "Dane osobowe" studies: "Dane kierunkowe" + contact: + title: $t(contact) + field: + content: "Treść" internship: fields: start-date: Data rozpoczęcia praktyki @@ -128,6 +135,8 @@ internship: intern: semester: semestr {{ semester, roman }} album: "numer albumu {{ album }}" + kind-requires-dean-approval: "Ten rodzaj praktyki/umowy wymaga akceptacji przez dziekana!" + duration-requires-dean-approval: "Taki okres trwania praktyki wymaga akceptacji przez dziekana!" date-range: "{{ start, DD MMMM YYYY }} - {{ end, DD MMMM YYYY }}" duration_2: "{{ duration, weeks }} tygodni" duration_0: "{{ duration, weeks }} tydzień" @@ -195,7 +204,7 @@ steps: draft: > W porozumieniu z firmą w której odbywają się praktyki należy sporządzić Indywidualny Plan Praktyk zgodnie z załączonym szablonem a następnie wysłać go do weryfikacji. Indywidualny Plan Praktyk musi zostać zatwierdzony - oraz podpisany przez Twojego zakłądowego opiekuna praktyki. + oraz podpisany przez Twojego zakładowego opiekuna praktyki. awaiting: > Twój indywidualny program praktyki został poprawnie zapisany w systemie. Musi on jeszcze zostać zweryfikowany i zatwierdzony. Po weryfikacji zostaniesz poinformowany o akceptacji bądź konieczności wprowadzenia zmian.