Forms for editiing reporting form

This commit is contained in:
Kacper Donat 2021-01-02 23:11:33 +01:00
parent 7f71e6758c
commit bb7887cb93
13 changed files with 489 additions and 121 deletions

View File

@ -1,6 +1,5 @@
import { AsyncResult } from "@/hooks";
import React from "react";
import { CircularProgress } from "@material-ui/core";
import { Alert } from "@material-ui/lab";
import { Loading } from "@/components/loading";

View File

@ -4,34 +4,38 @@ interface PredefinedChoices {
choices: Multilingual<string>[];
}
export interface BaseField {
export interface BaseFieldDefinition {
name: string;
description: Multilingual<string>;
label: Multilingual<string>;
}
export interface TextField extends BaseField {
type: "text";
value: string | null;
export interface TextFieldDefinition extends BaseFieldDefinition {
type: "short-text" | "long-text";
}
export interface MultiChoiceField extends BaseField, PredefinedChoices {
type: "multi-choice";
value: Multilingual<string>[] | null;
export type TextFieldValue = string;
export interface MultiChoiceFieldDefinition extends BaseFieldDefinition, PredefinedChoices {
type: "checkbox";
}
export interface SingleChoiceField extends BaseField, PredefinedChoices {
type: "single-choice";
value: Multilingual<string> | null
export type MultiChoiceValue = Multilingual<string>[];
export interface SingleChoiceFieldDefinition extends BaseFieldDefinition, PredefinedChoices {
type: "radio" | "select";
}
export interface SelectField extends BaseField, PredefinedChoices {
type: "select";
value: Multilingual<string> | null
}
export type SingleChoiceValue = Multilingual<string>;
export type ReportField = TextField | MultiChoiceField | SingleChoiceField | SelectField;
export type ReportFieldDefinition = TextFieldDefinition | MultiChoiceFieldDefinition | SingleChoiceFieldDefinition;
export type ReportFieldValue = TextFieldValue | MultiChoiceValue | SingleChoiceValue;
export type ReportFieldValues = { [field: string]: ReportFieldValue };
export type ReportSchema = ReportFieldDefinition[];
export interface Report {
fields: ReportField[];
fields: ReportFieldValues;
}
export const reportFieldTypes = ["short-text", "long-text", "checkbox", "radio", "select"];

View File

@ -1,5 +1,5 @@
import React from "react";
import { emptyReport } from "@/provider/dummy/report";
import { emptyReport, sampleReportSchema } from "@/provider/dummy/report";
import {
Button,
FormControl,
@ -18,22 +18,23 @@ import { Actions } from "@/components";
import { Link as RouterLink } from "react-router-dom";
import { route } from "@/routing";
import { useTranslation } from "react-i18next";
import { MultiChoiceField, Report, ReportField, SelectField, SingleChoiceField } from "@/data/report";
import { MultiChoiceFieldDefinition, Report, ReportFieldDefinition, ReportFieldValues, SingleChoiceFieldDefinition } from "@/data/report";
import { TextField as TextFieldFormik } from "formik-material-ui";
import { Field, Form, Formik, useFormik, useFormikContext } from "formik";
import { Multilingual } from "@/data";
import { Transformer } from "@/serialization";
export type ReportFieldProps<TField = ReportField> = {
export type ReportFieldProps<TField = ReportFieldDefinition> = {
field: TField;
}
const CustomField = ({ field, ...props }: ReportFieldProps) => {
export const CustomField = ({ field, ...props }: ReportFieldProps) => {
switch (field.type) {
case "text":
case "short-text":
case "long-text":
return <CustomField.Text {...props} field={ field } />
case "single-choice":
case "multi-choice":
case "checkbox":
case "radio":
return <CustomField.Choice {...props} field={ field }/>
case "select":
return <CustomField.Select {...props} field={ field }/>
@ -42,12 +43,16 @@ const CustomField = ({ field, ...props }: ReportFieldProps) => {
CustomField.Text = ({ field }: ReportFieldProps) => {
return <>
<Field label={ field.label.pl } name={ field.name } fullWidth component={ TextFieldFormik }/>
<Field label={ field.label.pl } name={ field.name }
fullWidth
rows={ field.type == "long-text" ? 4 : 1 } multiline={ field.type == "long-text" }
component={ TextFieldFormik }
/>
<Typography variant="caption" color="textSecondary" dangerouslySetInnerHTML={{ __html: field.description.pl }}/>
</>
}
CustomField.Select = ({ field }: ReportFieldProps<SelectField>) => {
CustomField.Select = ({ field }: ReportFieldProps<SingleChoiceFieldDefinition>) => {
const { t } = useTranslation();
const id = `custom-field-${field.name}`;
const { values, setFieldValue } = useFormikContext<any>();
@ -63,24 +68,24 @@ CustomField.Select = ({ field }: ReportFieldProps<SelectField>) => {
</FormControl>
}
CustomField.Choice = ({ field }: ReportFieldProps<SingleChoiceField | MultiChoiceField>) => {
CustomField.Choice = ({ field }: ReportFieldProps<SingleChoiceFieldDefinition | MultiChoiceFieldDefinition>) => {
const { t } = useTranslation();
const { values, setFieldValue } = useFormikContext<any>();
const value = values[field.name];
const isSelected = field.type == 'single-choice'
const isSelected = field.type == 'radio'
? (checked: Multilingual<string>) => value == checked
: (checked: Multilingual<string>) => (value || []).includes(checked)
const handleChange = field.type == 'single-choice'
const handleChange = field.type == 'radio'
? (choice: Multilingual<string>) => () => setFieldValue(field.name, choice, false)
: (choice: Multilingual<string>) => () => {
const current = value || [];
setFieldValue(field.name, !current.includes(choice) ? [ ...current, choice ] : current.filter((c: Multilingual<string>) => c != choice), false);
}
const Component = field.type == 'single-choice' ? Radio : Checkbox;
const Component = field.type == 'radio' ? Radio : Checkbox;
return <FormControl component="fieldset">
<FormLabel component="legend">{ field.label.pl }</FormLabel>
@ -94,19 +99,20 @@ CustomField.Choice = ({ field }: ReportFieldProps<SingleChoiceField | MultiChoic
</FormControl>
}
export type ReportFormValues = { [field: string]: any };
export type ReportFormValues = ReportFieldValues;
const reportToFormValuesTransformer: Transformer<Report, ReportFormValues, { report: Report }> = {
reverseTransform(subject: ReportFormValues, context: { report: Report }): Report {
return { ...context.report };
return { ...context.report, fields: subject };
},
transform(subject: Report, context: undefined): ReportFormValues {
return Object.fromEntries(subject.fields.map(field => [ field.name, field.value ]));
return subject.fields;
}
}
export default function ReportForm() {
const report = emptyReport;
const schema = sampleReportSchema;
const { t } = useTranslation();
const handleSubmit = async () => {};
@ -117,7 +123,7 @@ export default function ReportForm() {
<Grid item xs={12}>
<Typography variant="body1" component="p">{ t('forms.report.instructions') }</Typography>
</Grid>
{ report.fields.map(field => <Grid item xs={12}><CustomField field={ field }/></Grid>) }
{ schema.map(field => <Grid item xs={12}><CustomField field={ field }/></Grid>) }
<Grid item xs={12}>
<Actions>
<Button variant="contained" color="primary" onClick={ submitForm }>

View File

@ -1,23 +1,24 @@
import React, { useCallback, useEffect } from "react";
import React, { useCallback } from "react";
import { Page } from "@/pages/base";
import { useTranslation } from "react-i18next";
import { useAsync, useAsyncState } from "@/hooks";
import { useAsync } from "@/hooks";
import api from "@/management/api";
import { Async } from "@/components/async";
import { Container, Typography } from "@material-ui/core";
import MaterialTable, { Action, Column } from "material-table";
import { Container, IconButton, Tooltip, Typography } from "@material-ui/core";
import MaterialTable, { Column } from "material-table";
import { Edition } from "@/data/edition";
import { Pencil } from "mdi-material-ui";
import { FileFind } from "mdi-material-ui";
import { Management } from "../main";
import { createPortal } from "react-dom";
import { EditStaticPageDialog } from "@/management/page/create";
import { useHistory } from "react-router-dom";
import { route } from "@/routing";
import { actionsColumn } from "@/management/common/helpers";
export type EditionDetailsProps = {
edition: string;
}
export function EditionDetails({ edition, ...props }: EditionDetailsProps) {
const result = useAsync(useCallback(() => api.edition.details(edition), [ edition ]));
const result = useAsync(useCallback(() => api.edition.details(edition), [edition]));
return <Async async={ result }>{ edition => <pre>{ JSON.stringify(edition, null, 2) }</pre> }</Async>
}
@ -26,6 +27,15 @@ export function EditionsManagement() {
const { t } = useTranslation("management");
const editions = useAsync(useCallback(api.edition.all, []));
const ManageEditionAction = ({ edition }: { edition: Edition }) => {
const history = useHistory();
const handlePagePreview = async () => history.push(route('management:edition_manage', { edition: edition.id || "" }));
return <Tooltip title={ t("actions.manage") as string }>
<IconButton onClick={ handlePagePreview }><FileFind/></IconButton>
</Tooltip>;
}
const columns: Column<Edition>[] = [
{
title: t("edition.field.id"),
@ -47,13 +57,9 @@ export function EditionsManagement() {
customSort: (a, b) => a.course.name.localeCompare(b.course.name),
render: edition => edition.course.name,
},
]
const actions: Action<Edition>[] = [
{
icon: () => <Pencil />,
onClick: () => {},
}
actionsColumn(edition => <>
<ManageEditionAction edition={ edition }/>
</>),
]
return <Page>
@ -69,10 +75,9 @@ export function EditionsManagement() {
<MaterialTable
columns={ columns }
data={ editions }
actions={ actions }
detailPanel={ edition => <EditionDetails edition={ edition.id as string } /> }
detailPanel={ edition => <EditionDetails edition={ edition.id as string }/> }
title={ t("edition.index.title") }
options={{ search: false, actionsColumnIndex: -1 }}
options={ { search: false } }
/>
}
</Async>

View File

@ -0,0 +1,82 @@
import React, { useCallback } from "react";
import { useTranslation } from "react-i18next";
import { useRouteMatch } from "react-router-dom";
import { useAsync } from "@/hooks";
import api from "@/management/api";
import { Page } from "@/pages/base";
import { Container, Paper, Typography } from "@material-ui/core";
import { Management, ManagementLink } from "@/management/main";
import { Edition } from "@/data/edition";
import { Async } from "@/components/async";
import { AccountMultiple, BriefcaseAccount, CertificateOutline, CogOutline, FileChartOutline, FormatPageBreak } from "mdi-material-ui";
import { route } from "@/routing";
import { useSpacing } from "@/styles";
import { createStyles, makeStyles, Theme } from "@material-ui/core/styles";
const useSectionStyles = makeStyles((theme: Theme) => createStyles({
header: {
padding: theme.spacing(2),
paddingBottom: 0,
fontWeight: 'bold',
color: theme.palette.grey["800"],
},
}))
export function title(edition: Edition) {
return `${edition.course.name} - ${edition.startDate.year()}`
}
export const ManageEditionPage = () => {
const { t } = useTranslation("management");
const { params } = useRouteMatch();
const edition = useAsync<Edition>(useCallback(() => api.edition.details(params.edition), [params.edition]))
const spacing = useSpacing(2);
const classes = useSectionStyles();
return <Page>
<Async async={ edition }>{ edition =>
<>
<Page.Header>
<Management.Breadcrumbs>
<Typography color="textPrimary">{ title(edition) }</Typography>
</Management.Breadcrumbs>
<Page.Title>{ title(edition) }</Page.Title>
</Page.Header>
<Container className={ spacing.vertical }>
<Paper elevation={ 2 }>
<Typography className={ classes.header }>{ t("edition.manage.internships") }</Typography>
<Management.Menu>
<ManagementLink icon={ <AccountMultiple/> } route={ route("management:edition_report_form", { edition: edition.id || "" }) }>
{ t("management:edition.students.title") }
</ManagementLink>
<ManagementLink icon={ <BriefcaseAccount/> } route={ route("management:edition_report_form", { edition: edition.id || "" }) }>
{ t("management:edition.internships.title") }
</ManagementLink>
<ManagementLink icon={ <FormatPageBreak/> } route={ route("management:edition_report_form", { edition: edition.id || "" }) }>
{ t("management:edition.ipp.title") }
</ManagementLink>
<ManagementLink icon={ <FileChartOutline/> } route={ route("management:edition_report_form", { edition: edition.id || "" }) }>
{ t("management:edition.reports.title") }
</ManagementLink>
<ManagementLink icon={ <CertificateOutline/> } route={ route("management:edition_report_form", { edition: edition.id || "" }) }>
{ t("management:edition.dean-approvals.title") }
</ManagementLink>
</Management.Menu>
</Paper>
<Paper elevation={ 2 }>
<Typography className={ classes.header }>{ t("edition.manage.management") }</Typography>
<Management.Menu>
<ManagementLink icon={ <FormatPageBreak/> } route={ route("management:edition_report_form", { edition: edition.id || "" }) }>
{ t("management:edition.report-form.title") }
</ManagementLink>
<ManagementLink icon={ <CogOutline/> } route={ route("management:edition_settings", { edition: edition.id || "" }) }>
{ t("management:edition.settings.title") }
</ManagementLink>
</Management.Menu>
</Paper>
</Container>
</>
}</Async>
</Page>
}

View File

@ -0,0 +1,45 @@
import React from "react";
import { Button, Dialog, DialogActions, DialogContent, DialogProps, DialogTitle } from "@material-ui/core";
import { ReportFieldDefinition } from "@/data/report";
import { useTranslation } from "react-i18next";
import { useSpacing } from "@/styles";
import { Form, Formik } from "formik";
import { Actions } from "@/components";
import { Save } from "@material-ui/icons";
import { Cancel } from "mdi-material-ui";
import { FieldDefinitionForm, FieldDefinitionFormValues, fieldFormValuesTransformer, initialFieldFormValues } from "@/management/edition/report/fields/form";
export type EditFieldDialogProps = {
onSave?: (field: ReportFieldDefinition) => void;
field?: ReportFieldDefinition;
} & DialogProps;
export function EditFieldDefinitionDialog({ onSave, field, ...props }: EditFieldDialogProps) {
const { t } = useTranslation("management");
const spacing = useSpacing(3);
const handleSubmit = (values: FieldDefinitionFormValues) => {
onSave?.(fieldFormValuesTransformer.reverseTransform(values));
};
const initialValues = field
? fieldFormValuesTransformer.transform(field)
: initialFieldFormValues;
return <Dialog { ...props } maxWidth="md">
<Formik initialValues={ initialValues } onSubmit={ handleSubmit }>
<Form className={ spacing.vertical }>
<DialogTitle>{ t(field ? "report-field.edit.title" : "report-field.create.title") }</DialogTitle>
<DialogContent>
<FieldDefinitionForm />
</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>
}

View File

@ -0,0 +1,104 @@
import React from "react";
import { ReportFieldDefinition, reportFieldTypes } from "@/data/report";
import { identityTransformer, Transformer } from "@/serialization";
import { useTranslation } from "react-i18next";
import { useSpacing } from "@/styles";
import { Field, FieldArray, FieldProps, useFormikContext } from "formik";
import { TextField as TextFieldFormik, Select } from "formik-material-ui";
import { FormControl, InputLabel, Typography, MenuItem, Card, Box, Button, CardContent, CardHeader, IconButton } from "@material-ui/core";
import { CKEditorField } from "@/field/ckeditor";
import { Multilingual } from "@/data";
import { Actions } from "@/components";
import { Add } from "@material-ui/icons";
import { TrashCan } from "mdi-material-ui";
import { FieldPreview } from "@/management/edition/report/fields/list";
import { createStyles, makeStyles, Theme } from "@material-ui/core/styles";
export type FieldDefinitionFormValues = ReportFieldDefinition | { type: string };
export const initialFieldFormValues: FieldDefinitionFormValues = {
type: "short-text",
name: "",
description: {
pl: "",
en: "",
},
label: {
pl: "",
en: "",
},
choices: [],
}
export const fieldFormValuesTransformer: Transformer<ReportFieldDefinition, FieldDefinitionFormValues> = identityTransformer;
export type ChoiceFieldProps = { name: string };
const ChoiceField = ({ field, form, meta }: FieldProps) => {
const { name } = field;
const { t } = useTranslation("management");
const spacing = useSpacing(2);
return <div className={ spacing.vertical }>
<Field label={ t("translation:language.pl") } name={`${name}.pl`} fullWidth component={ TextFieldFormik }/>
<Field label={ t("translation:language.en") } name={`${name}.en`} fullWidth component={ TextFieldFormik }/>
</div>
}
const useStyles = makeStyles((theme: Theme) => createStyles({
preview: {
padding: theme.spacing(2),
backgroundColor: "#e9f0f5",
},
}))
export function FieldDefinitionForm() {
const { t } = useTranslation("management");
const spacing = useSpacing(2);
const { values } = useFormikContext<any>();
const classes = useStyles();
return <div className={ spacing.vertical }>
<Field label={ t("report-field.field.name") } name="name" fullWidth component={ TextFieldFormik }/>
<FormControl variant="outlined">
<InputLabel htmlFor="report-field-type">{ t("report-field.field.type") }</InputLabel>
<Field
component={Select}
name="type"
label={ t("report-field.field.name") }
inputProps={{ id: 'report-field-type', }}
>
{ reportFieldTypes.map(type => <MenuItem value={ type }>{ t(`report-field.type.${type}`) }</MenuItem>)}
</Field>
</FormControl>
<Typography variant="subtitle2">{ t("report-field.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("report-field.field.description") }</Typography>
<Field label={ t("translation:language.pl") } name="description.pl" fullWidth component={ CKEditorField }/>
<Field label={ t("translation:language.en") } name="description.en" fullWidth component={ CKEditorField }/>
{ ["radio", "select", "checkbox"].includes(values.type) && <>
<Typography variant="subtitle2">{ t("report-field.field.choices") }</Typography>
<FieldArray name="choices" render={ helper => <>
{ values.choices.map((value: Multilingual<string>, index: number) => <Card>
<CardHeader subheader={ t("report-field.field.choice", { index }) } action={ <>
<IconButton onClick={ () => helper.remove(index) }>
<TrashCan />
</IconButton>
</> }/>
<CardContent>
<Field name={`choices[${index}]`} component={ ChoiceField } />
</CardContent>
</Card>) }
<Actions>
<Button variant="contained" startIcon={ <Add /> } color="primary" onClick={() => helper.push({ pl: "", en: "" })}>{ t("actions.add") }</Button>
</Actions>
</> } />
</> }
<div className={ classes.preview }>
<Typography variant="subtitle2">{ t("report-field.preview") }</Typography>
<FieldPreview field={ fieldFormValuesTransformer.reverseTransform(values) }/>
</div>
</div>
}

View File

@ -0,0 +1,84 @@
import React, { useState } from "react";
import { Page } from "@/pages/base";
import { Management } from "@/management/main";
import { Box, Container, IconButton, Tooltip, Typography } from "@material-ui/core";
import { useTranslation } from "react-i18next";
import { sampleReportSchema } from "@/provider/dummy/report";
import MaterialTable, { Column } from "material-table";
import { actionsColumn, fieldComparator, multilingualStringComparator } from "@/management/common/helpers";
import { MultilingualCell } from "@/management/common/MultilangualCell";
import { ReportFieldDefinition } from "@/data/report";
import { Formik } from "formik";
import { CustomField } from "@/forms/report";
import { Edit } from "@material-ui/icons";
import { createPortal } from "react-dom";
import { createDeleteAction } from "@/management/common/DeleteResourceAction";
import { EditFieldDefinitionDialog } from "@/management/edition/report/fields/edit";
const title = "edition.report-fields.title";
export const FieldPreview = ({ field }: { field: ReportFieldDefinition }) => {
return <Formik initialValues={{}} onSubmit={() => {}}>
<CustomField field={ field }/>
</Formik>
}
export const EditionReportFields = () => {
const { t } = useTranslation("management");
const schema = sampleReportSchema;
const handleFieldDeletion = () => {}
const DeleteFieldAction = createDeleteAction<ReportFieldDefinition>({ label: field => field.label.pl, onDelete: handleFieldDeletion })
const EditFieldAction = ({ field }: { field: ReportFieldDefinition }) => {
const [ open, setOpen ] = useState<boolean>(false);
const handleFieldSave = async (field: ReportFieldDefinition) => {
}
return <>
<Tooltip title={ t("actions.edit") as any }>
<IconButton onClick={ () => setOpen(true) }><Edit /></IconButton>
</Tooltip>
{ open && createPortal(
<EditFieldDefinitionDialog open={ open } onSave={ handleFieldSave } field={ field } onClose={ () => setOpen(false) }/>,
document.getElementById("modals") as Element
) }
</>
}
const columns: Column<ReportFieldDefinition>[] = [
{
title: t("report-field.field.label"),
customSort: fieldComparator('label', multilingualStringComparator),
cellStyle: { whiteSpace: "nowrap" },
render: field => <MultilingualCell value={ field.label }/>,
},
{
title: t("report-field.field.type"),
cellStyle: { whiteSpace: "nowrap" },
render: field => t(`report-field.type.${field.type}`),
},
actionsColumn(field => <>
<EditFieldAction field={ field }/>
<DeleteFieldAction resource={ field }/>
</>),
]
return <Page>
<Page.Header maxWidth="lg">
<Management.Breadcrumbs>
<Typography color="textPrimary">{ t(title) }</Typography>
</Management.Breadcrumbs>
<Page.Title>{ t(title) }</Page.Title>
</Page.Header>
<Container maxWidth="lg">
<MaterialTable
columns={ columns }
data={ schema }
title={ t(title) }
detailPanel={ field => <Box p={3}><FieldPreview field={ field } /></Box> }
/>
</Container>
</Page>
}

View File

@ -6,6 +6,13 @@ import { route } from "@/routing";
import { useTranslation } from "react-i18next";
import { CalendarClock, FileCertificateOutline, FileDocumentMultipleOutline } from "mdi-material-ui";
export const ManagementLink = ({ icon, route, children }: ManagementLinkProps) =>
<ListItem button component={ RouterLink } to={ route }>
<ListItemIcon>{ icon }</ListItemIcon>
<ListItemText>{ children }</ListItemText>
</ListItem>
export const Management = {
Breadcrumbs: ({ children, ...props }: BreadcrumbsProps) => {
const { t } = useTranslation();
@ -14,7 +21,9 @@ export const Management = {
<Link component={ RouterLink } to={ route("management:index") }>{ t("management:title") }</Link>
{ children }
</Page.Breadcrumbs>;
}
},
Menu: List,
MenuItem: ManagementLink,
}
type ManagementLinkProps = React.PropsWithChildren<{
@ -22,12 +31,6 @@ type ManagementLinkProps = React.PropsWithChildren<{
route: string,
}>;
const ManagementLink = ({ icon, route, children }: ManagementLinkProps) =>
<ListItem button component={ RouterLink } to={ route }>
<ListItemIcon>{ icon }</ListItemIcon>
<ListItemText>{ children }</ListItemText>
</ListItem>
export const ManagementIndex = () => {
const { t } = useTranslation();
@ -37,7 +40,7 @@ export const ManagementIndex = () => {
</Page.Header>
<Container>
<Paper elevation={ 2 }>
<List>
<Management.Menu>
<ManagementLink icon={ <CalendarClock /> } route={ route("management:editions") }>
{ t("management:edition.index.title") }
</ManagementLink>
@ -47,7 +50,7 @@ export const ManagementIndex = () => {
<ManagementLink icon={ <FileDocumentMultipleOutline /> } route={ route("management:static_pages") }>
{ t("management:page.index.title") }
</ManagementLink>
</List>
</Management.Menu>
</Paper>
</Container>
</Page>

View File

@ -5,11 +5,16 @@ import React from "react";
import { ManagementIndex } from "@/management/main";
import StaticPageManagement from "@/management/page/list";
import { InternshipTypeManagement } from "@/management/type/list";
import { ManageEditionPage } from "@/management/edition/manage";
import { EditionReportFields } from "@/management/edition/report/fields/list";
export const managementRoutes: Route[] = ([
{ name: "index", path: "/", content: ManagementIndex, exact: true },
{ name: "edition_report_form", path: "/editions/:edition/report", content: EditionReportFields },
{ name: "edition_manage", path: "/editions/:edition", content: ManageEditionPage },
{ name: "editions", path: "/editions", content: EditionsManagement },
{ name: "types", path: "/types", content: InternshipTypeManagement },
{ name: "static_pages", path: "/static-pages", content: StaticPageManagement }
] as Route[]).map(

View File

@ -17,10 +17,8 @@ import { BulkActions } from "@/management/common/BulkActions";
import { useSpacing } from "@/styles";
import { Actions } from "@/components";
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";

View File

@ -1,66 +1,80 @@
import { Report } from "@/data/report";
import { Report, ReportSchema } from "@/data/report";
const choices = [1, 2, 3, 4, 5].map(n => ({
pl: `Wybór ${n}`,
en: `Choice ${n}`
}))
export const sampleReportSchema: ReportSchema = [
{
type: "short-text",
name: "short",
description: {
en: "Text field, with <strong>HTML</strong> description",
pl: "Pole tekstowe, z opisem w formacie <strong>HTML</strong>"
},
label: {
en: "Text Field",
pl: "Pole tekstowe",
},
},
{
type: "long-text",
name: "long",
description: {
en: "Long text field, with <strong>HTML</strong> description",
pl: "Długie pole tekstowe, z opisem w formacie <strong>HTML</strong>"
},
label: {
en: "Long Text Field",
pl: "Długie Pole tekstowe",
},
},
{
type: "radio",
name: "radio",
description: {
en: "single choice field, with <strong>HTML</strong> description",
pl: "Pole jednokrotnego wyboru, z opisem w formacie <strong>HTML</strong>"
},
choices,
label: {
en: "Single choice field",
pl: "Pole jednokrotnego wyboru",
},
},
{
type: "select",
name: "select",
description: {
en: "select field, with <strong>HTML</strong> description",
pl: "Pole jednokrotnego wyboru z selectboxem, z opisem w formacie <strong>HTML</strong>"
},
choices,
label: {
en: "Select field",
pl: "Pole jednokrotnego wyboru (selectbox)",
},
},
{
name: "multi",
type: "checkbox",
description: {
en: "Multiple choice field, with <strong>HTML</strong> description",
pl: "Pole wielokrotnego wyboru, z opisem w formacie <strong>HTML</strong>"
},
choices,
label: {
en: "Multi choice field",
pl: "Pole wielokrotnego wyboru",
},
},
]
export const emptyReport: Report = {
fields: [
{
type: "text",
name: "text",
description: {
en: "Text field, with <strong>HTML</strong> description",
pl: "Pole tekstowe, z opisem w formacie <strong>HTML</strong>"
},
value: null,
label: {
en: "Text Field",
pl: "Pole tekstowe",
},
},
{
type: "single-choice",
name: "single",
description: {
en: "single choice field, with <strong>HTML</strong> description",
pl: "Pole jednokrotnego wyboru, z opisem w formacie <strong>HTML</strong>"
},
value: null,
choices,
label: {
en: "Single choice field",
pl: "Pole jednokrotnego wyboru",
},
},
{
type: "select",
name: "select",
description: {
en: "select field, with <strong>HTML</strong> description",
pl: "Pole jednokrotnego wyboru z selectboxem, z opisem w formacie <strong>HTML</strong>"
},
value: choices[2],
choices,
label: {
en: "Select field",
pl: "Pole jednokrotnego wyboru (selectbox)",
},
},
{
name: "multi",
type: "multi-choice",
description: {
en: "Multiple choice field, with <strong>HTML</strong> description",
pl: "Pole wielokrotnego wyboru, z opisem w formacie <strong>HTML</strong>"
},
value: [ choices[0], choices[3] ],
choices,
label: {
en: "Multi choice field",
pl: "Pole wielokrotnego wyboru",
},
},
]
fields: {
"short": "Testowa wartość",
"select": choices[0],
"multi": [ choices[1], choices[2] ],
}
}

View File

@ -11,6 +11,7 @@ actions:
preview: Podgląd
delete: Usuń
edit: Edytuj
add: Dodaj
edition:
index:
@ -20,6 +21,24 @@ edition:
start: Początek
end: Koniec
course: Kierunek
report-fields:
title: "Pola formularza raportu praktyki"
report-field:
preview: Podgląd
field:
type: "Rodzaj"
name: "Unikalny identyfikator"
label: "Etykieta"
description: "Opis"
choices: "Możliwe wybory"
choice: "Wybór #{{ index }}"
type:
select: "Pole wyboru"
radio: "Jednokrotny wybór (radio)"
checkbox: "Wielokrotny wybór (checkboxy)"
short-text: "Pole krótkiej odpowiedzi"
long-text: "Pole długiej odpowiedzi"
type:
index: