Add ability to update and add internship types
This commit is contained in:
parent
092171d27f
commit
f3fd265dad
28
src/management/common/LabelWithIcon.tsx
Normal file
28
src/management/common/LabelWithIcon.tsx
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { createStyles, makeStyles, Theme } from "@material-ui/core/styles";
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme: Theme) => createStyles({
|
||||||
|
root: {
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center"
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
marginRight: theme.spacing(1),
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center"
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
export type LabelWithIconProps = {
|
||||||
|
icon: React.ReactNode,
|
||||||
|
children: React.ReactChildren,
|
||||||
|
}
|
||||||
|
|
||||||
|
export function LabelWithIcon({ icon, children }: LabelWithIconProps) {
|
||||||
|
const classes = useStyles();
|
||||||
|
|
||||||
|
return <div className={ classes.root }>
|
||||||
|
<div className={ classes.icon }>{ icon }</div>
|
||||||
|
{ children }
|
||||||
|
</div>
|
||||||
|
}
|
47
src/management/type/edit.tsx
Normal file
47
src/management/type/edit.tsx
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { Button, Dialog, DialogActions, DialogContent, DialogProps, DialogTitle } from "@material-ui/core";
|
||||||
|
import React from "react";
|
||||||
|
import { Form, Formik } from "formik";
|
||||||
|
import { initialStaticPageFormValues, StaticPageForm, StaticPageFormValues, staticPageFormValuesTransformer } from "@/management/page/form";
|
||||||
|
import { Actions } from "@/components";
|
||||||
|
import { Save } from "@material-ui/icons";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { Cancel } from "mdi-material-ui";
|
||||||
|
import { useSpacing } from "@/styles";
|
||||||
|
import { initialInternshipTypeFormValues, InternshipTypeForm, InternshipTypeFormValues, internshipTypeFormValuesTransformer } from "@/management/type/form";
|
||||||
|
import { InternshipType } from "@/data";
|
||||||
|
|
||||||
|
export type EditInternshipTypeDialogProps = {
|
||||||
|
onSave?: (page: InternshipType) => void;
|
||||||
|
value?: InternshipType;
|
||||||
|
} & DialogProps;
|
||||||
|
|
||||||
|
export function EditInternshipTypeDialog({ onSave, value, ...props }: EditInternshipTypeDialogProps) {
|
||||||
|
const { t } = useTranslation("management");
|
||||||
|
const spacing = useSpacing(3);
|
||||||
|
|
||||||
|
const handleSubmit = (values: InternshipTypeFormValues) => {
|
||||||
|
onSave?.(internshipTypeFormValuesTransformer.reverseTransform(values));
|
||||||
|
};
|
||||||
|
|
||||||
|
const initialValues = value
|
||||||
|
? internshipTypeFormValuesTransformer.transform(value)
|
||||||
|
: initialInternshipTypeFormValues;
|
||||||
|
|
||||||
|
return <Dialog { ...props } maxWidth="lg">
|
||||||
|
<Formik initialValues={ initialValues } onSubmit={ handleSubmit }>
|
||||||
|
<Form className={ spacing.vertical }>
|
||||||
|
<DialogTitle>{ t(value ? "type.edit.title" : "type.create.title") }</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<InternshipTypeForm />
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Actions>
|
||||||
|
<Button variant="contained" color="primary" startIcon={ <Save /> } type="submit">{ t("save") }</Button>
|
||||||
|
<Button startIcon={ <Cancel /> } onClick={ ev => props.onClose?.(ev, "escapeKeyDown") }>{ t("cancel") }</Button>
|
||||||
|
</Actions>
|
||||||
|
</DialogActions>
|
||||||
|
</Form>
|
||||||
|
</Formik>
|
||||||
|
</Dialog>
|
||||||
|
}
|
||||||
|
|
55
src/management/type/form.tsx
Normal file
55
src/management/type/form.tsx
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { InternshipType } from "@/data";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { useSpacing } from "@/styles";
|
||||||
|
import { Field } from "formik";
|
||||||
|
import { TextField as TextFieldFormik, Checkbox as CheckboxFormik } from "formik-material-ui";
|
||||||
|
import { FormControlLabel, FormGroup, Typography } from "@material-ui/core";
|
||||||
|
import { CKEditorField } from "@/field/ckeditor";
|
||||||
|
import { AccountCheck, ShieldCheck } from "mdi-material-ui";
|
||||||
|
import { identityTransformer, Transformer } from "@/serialization";
|
||||||
|
import { LabelWithIcon } from "@/management/common/LabelWithIcon";
|
||||||
|
|
||||||
|
export type InternshipTypeFormValues = Omit<InternshipType, 'id'>;
|
||||||
|
|
||||||
|
export const initialInternshipTypeFormValues: InternshipTypeFormValues = {
|
||||||
|
label: {
|
||||||
|
pl: "",
|
||||||
|
en: "",
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
pl: "",
|
||||||
|
en: "",
|
||||||
|
},
|
||||||
|
requiresInsurance: false,
|
||||||
|
requiresDeanApproval: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const internshipTypeFormValuesTransformer: Transformer<InternshipType, InternshipTypeFormValues> = identityTransformer;
|
||||||
|
|
||||||
|
export function InternshipTypeForm() {
|
||||||
|
const { t } = useTranslation("management");
|
||||||
|
const spacing = useSpacing(2);
|
||||||
|
|
||||||
|
return <div className={ spacing.vertical }>
|
||||||
|
<Typography variant="subtitle2">{ t("type.field.label") }</Typography>
|
||||||
|
<Field label={ t("translation:language.pl") } name="label.pl" fullWidth component={ TextFieldFormik }/>
|
||||||
|
<Field label={ t("translation:language.en") } name="label.en" fullWidth component={ TextFieldFormik }/>
|
||||||
|
<Typography variant="subtitle2">{ t("type.field.description") }</Typography>
|
||||||
|
<Field label={ t("translation:language.pl") } name="description.pl" fullWidth component={ TextFieldFormik }/>
|
||||||
|
<Field label={ t("translation:language.en") } name="description.en" fullWidth component={ TextFieldFormik }/>
|
||||||
|
|
||||||
|
<Typography variant="subtitle2">{ t("type.field.flags") }</Typography>
|
||||||
|
<FormGroup>
|
||||||
|
<FormControlLabel
|
||||||
|
control={ <Field name="requiresDeanApproval" component={ CheckboxFormik }/> }
|
||||||
|
label={ <LabelWithIcon icon={ <AccountCheck /> }>{ t("type.flag.dean-approval") }</LabelWithIcon> }
|
||||||
|
/>
|
||||||
|
<FormControlLabel
|
||||||
|
control={ <Field name="requiresInsurance" component={ CheckboxFormik }/> }
|
||||||
|
label={ <LabelWithIcon icon={ <ShieldCheck /> }>{ t("type.flag.insurance") }</LabelWithIcon> }
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
}
|
@ -5,7 +5,7 @@ import { useAsyncState } from "@/hooks";
|
|||||||
import { InternshipType } from "@/data";
|
import { InternshipType } from "@/data";
|
||||||
import api from "@/management/api";
|
import api from "@/management/api";
|
||||||
import { Management } from "@/management/main";
|
import { Management } from "@/management/main";
|
||||||
import { Button, Container, Tooltip, Typography } from "@material-ui/core";
|
import { Button, Container, IconButton, Tooltip, Typography } from "@material-ui/core";
|
||||||
import { Async } from "@/components/async";
|
import { Async } from "@/components/async";
|
||||||
import MaterialTable, { Column } from "material-table";
|
import MaterialTable, { Column } from "material-table";
|
||||||
import { MaterialTableTitle } from "@/management/common/MaterialTableTitle";
|
import { MaterialTableTitle } from "@/management/common/MaterialTableTitle";
|
||||||
@ -17,6 +17,11 @@ import { BulkActions } from "@/management/common/BulkActions";
|
|||||||
import { useSpacing } from "@/styles";
|
import { useSpacing } from "@/styles";
|
||||||
import { Actions } from "@/components";
|
import { Actions } from "@/components";
|
||||||
import { MultilingualCell } from "@/management/common/MultilangualCell";
|
import { MultilingualCell } from "@/management/common/MultilangualCell";
|
||||||
|
import { default as StaticPage } from "@/data/page";
|
||||||
|
import { Add, Edit } from "@material-ui/icons";
|
||||||
|
import { createPortal } from "react-dom";
|
||||||
|
import { EditStaticPageDialog } from "@/management/page/edit";
|
||||||
|
import { EditInternshipTypeDialog } from "@/management/type/edit";
|
||||||
|
|
||||||
const title = "type.index.title";
|
const title = "type.index.title";
|
||||||
|
|
||||||
@ -41,6 +46,44 @@ export const InternshipTypeManagement = () => {
|
|||||||
|
|
||||||
const DeleteTypeAction = createDeleteAction({ label, onDelete: handleTypeDelete });
|
const DeleteTypeAction = createDeleteAction({ label, onDelete: handleTypeDelete });
|
||||||
|
|
||||||
|
const CreateTypeAction = () => {
|
||||||
|
const [ open, setOpen ] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const handleTypeCreation = async (value: InternshipType) => {
|
||||||
|
await api.type.save(value);
|
||||||
|
setOpen(false);
|
||||||
|
updateTypeList();
|
||||||
|
}
|
||||||
|
|
||||||
|
return <>
|
||||||
|
<Button variant="contained" color="primary" startIcon={ <Add /> } onClick={ () => setOpen(true) }>{ t("create") }</Button>
|
||||||
|
{ open && createPortal(
|
||||||
|
<EditInternshipTypeDialog open={ open } onSave={ handleTypeCreation } onClose={ () => setOpen(false) }/>,
|
||||||
|
document.getElementById("modals") as Element
|
||||||
|
) }
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
|
||||||
|
const EditTypeAction = ({ resource }: { resource: InternshipType }) => {
|
||||||
|
const [ open, setOpen ] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const handleTypeCreation = async (value: InternshipType) => {
|
||||||
|
await api.type.save(value);
|
||||||
|
setOpen(false);
|
||||||
|
updateTypeList();
|
||||||
|
}
|
||||||
|
|
||||||
|
return <>
|
||||||
|
<Tooltip title={ t("actions.edit") as any }>
|
||||||
|
<IconButton onClick={ () => setOpen(true) }><Edit /></IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
{ open && createPortal(
|
||||||
|
<EditInternshipTypeDialog open={ open } onSave={ handleTypeCreation } value={ resource } onClose={ () => setOpen(false) }/>,
|
||||||
|
document.getElementById("modals") as Element
|
||||||
|
) }
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
|
||||||
const columns: Column<InternshipType>[] = [
|
const columns: Column<InternshipType>[] = [
|
||||||
{
|
{
|
||||||
field: "id",
|
field: "id",
|
||||||
@ -61,16 +104,17 @@ export const InternshipTypeManagement = () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("type.field.flags"),
|
title: t("type.field.flags"),
|
||||||
render: type => <>
|
render: type => <div style={{ display: "flex", flexDirection: "column" }}>
|
||||||
{ type.requiresDeanApproval && <Tooltip title={ t("type.flag.dean-approval") as string }><AccountCheck/></Tooltip> }
|
{ type.requiresDeanApproval && <Tooltip title={ t("type.flag.dean-approval") as string }><AccountCheck/></Tooltip> }
|
||||||
{ type.requiresInsurance && <Tooltip title={ t("type.flag.insurance") as string }><ShieldCheck/></Tooltip> }
|
{ type.requiresInsurance && <Tooltip title={ t("type.flag.insurance") as string }><ShieldCheck/></Tooltip> }
|
||||||
</>,
|
</div>,
|
||||||
width: 0,
|
width: 0,
|
||||||
filtering: true,
|
filtering: true,
|
||||||
sorting: false,
|
sorting: false,
|
||||||
},
|
},
|
||||||
actionsColumn(type => <>
|
actionsColumn(type => <>
|
||||||
<DeleteTypeAction resource={ type }/>
|
<DeleteTypeAction resource={ type }/>
|
||||||
|
<EditTypeAction resource={ type }/>
|
||||||
</>)
|
</>)
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -83,6 +127,7 @@ export const InternshipTypeManagement = () => {
|
|||||||
</Page.Header>
|
</Page.Header>
|
||||||
<Container maxWidth="lg" className={ spacing.vertical }>
|
<Container maxWidth="lg" className={ spacing.vertical }>
|
||||||
<Actions>
|
<Actions>
|
||||||
|
<CreateTypeAction />
|
||||||
<Button onClick={ updateTypeList } startIcon={ <Refresh /> }>{ t("refresh") }</Button>
|
<Button onClick={ updateTypeList } startIcon={ <Refresh /> }>{ t("refresh") }</Button>
|
||||||
</Actions>
|
</Actions>
|
||||||
{ selected.length > 0 && <BulkActions>
|
{ selected.length > 0 && <BulkActions>
|
||||||
|
@ -24,10 +24,14 @@ edition:
|
|||||||
type:
|
type:
|
||||||
index:
|
index:
|
||||||
title: "Rodzeje praktyki"
|
title: "Rodzeje praktyki"
|
||||||
|
edit:
|
||||||
|
title: "Edytuj rodzaj praktyki"
|
||||||
|
create:
|
||||||
|
title: "Utwórz rodzaj praktyki"
|
||||||
field:
|
field:
|
||||||
label: "Rodzaj praktyki"
|
label: "Rodzaj praktyki"
|
||||||
description: "Opis"
|
description: "Opis"
|
||||||
flags: "Flagi"
|
flags: "Wymogi"
|
||||||
flag:
|
flag:
|
||||||
dean-approval: "Wymaga zgody dziekana"
|
dean-approval: "Wymaga zgody dziekana"
|
||||||
insurance: "Wymaga ubezpieczenia"
|
insurance: "Wymaga ubezpieczenia"
|
||||||
|
Loading…
Reference in New Issue
Block a user