Intership listening
This commit is contained in:
parent
a38409e2d0
commit
e31d89b688
@ -1,10 +1,79 @@
|
||||
import React, { useState } from "react";
|
||||
import { Button, ButtonGroup, Dialog, DialogActions, DialogContent, DialogTitle, Menu, MenuItem, TextField, Typography } from "@material-ui/core";
|
||||
import {
|
||||
Button,
|
||||
ButtonGroup,
|
||||
ButtonProps,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogProps,
|
||||
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 AcceptSubmissionDialogProps = {
|
||||
onAccept: (comment?: string) => void;
|
||||
label: string;
|
||||
} & DialogProps;
|
||||
|
||||
export function AcceptSubmissionDialog({ onAccept, label, ...props }: AcceptSubmissionDialogProps) {
|
||||
const { t } = useTranslation();
|
||||
const [comment, setComment] = useState<string>("");
|
||||
const classes = useVerticalSpacing(3);
|
||||
|
||||
return <Dialog maxWidth="md" { ...props }>
|
||||
<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={ ev => props.onClose?.(ev, "backdropClick") }>
|
||||
{ t('cancel') }
|
||||
</Button>
|
||||
<Button onClick={ () => onAccept?.(comment) } color="primary" variant="contained">
|
||||
{ t('confirm') }
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
}
|
||||
|
||||
type DiscardSubmissionDialogProps = {
|
||||
onDiscard: (comment: string) => void;
|
||||
label: string;
|
||||
} & DialogProps;
|
||||
|
||||
export function DiscardSubmissionDialog({ onDiscard, label, ...props }: DiscardSubmissionDialogProps) {
|
||||
const { t } = useTranslation();
|
||||
const [comment, setComment] = useState<string>("");
|
||||
const classes = useVerticalSpacing(3);
|
||||
|
||||
return <Dialog maxWidth="md" { ...props }>
|
||||
<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={ ev => props.onClose?.(ev, "backdropClick") }>
|
||||
{ t('cancel') }
|
||||
</Button>
|
||||
<Button onClick={ () => onDiscard?.(comment) } color="primary" variant="contained">
|
||||
{ t('confirm') }
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
}
|
||||
|
||||
type AcceptanceActionsProps = {
|
||||
onAccept: (comment?: string) => void;
|
||||
onDiscard: (comment: string) => void;
|
||||
@ -17,19 +86,8 @@ export function AcceptanceActions({ onAccept, onDiscard, label }: AcceptanceActi
|
||||
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);
|
||||
}
|
||||
@ -77,39 +135,8 @@ export function AcceptanceActions({ onAccept, onDiscard, label }: AcceptanceActi
|
||||
</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>
|
||||
<DiscardSubmissionDialog open={ isDiscardModalOpen } onClose={ handleDiscardModalClose } maxWidth="md" onDiscard={ onDiscard } label={ label }/>
|
||||
<AcceptSubmissionDialog open={ isAcceptModalOpen } onClose={ handleAcceptModalClose } maxWidth="md" onAccept={ onAccept } label={ label }/>
|
||||
</>, document.getElementById("modals") as Element) }
|
||||
</>
|
||||
}
|
||||
|
@ -2,12 +2,14 @@ import * as edition from "./edition"
|
||||
import * as page from "./page"
|
||||
import * as type from "./type"
|
||||
import * as course from "./course"
|
||||
import * as internship from "./internship"
|
||||
|
||||
export const api = {
|
||||
edition,
|
||||
page,
|
||||
type,
|
||||
course,
|
||||
internship
|
||||
}
|
||||
|
||||
export default api;
|
||||
|
58
src/management/api/internship.ts
Normal file
58
src/management/api/internship.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import { Identifiable, Identifier, Internship } from "@/data";
|
||||
import { sampleCompanies, sampleStudent } from "@/provider/dummy";
|
||||
import moment, { Moment } from "moment-timezone";
|
||||
import { OneOrMany } from "@/helpers";
|
||||
import { SubmissionState, SubmissionStatus } from "@/state/reducer/submission";
|
||||
|
||||
export type InternshipSubmission = Internship & {
|
||||
state: SubmissionStatus,
|
||||
changed: Moment
|
||||
}
|
||||
|
||||
const sampleInternship: InternshipSubmission = {
|
||||
id: "test",
|
||||
company: sampleCompanies[0],
|
||||
startDate: moment('2020-07-01'),
|
||||
endDate: moment('2020-08-31'),
|
||||
hours: 180,
|
||||
intern: sampleStudent,
|
||||
isAccepted: false,
|
||||
lengthInWeeks: 0,
|
||||
mentor: {
|
||||
email: "test@test.com",
|
||||
name: "Test",
|
||||
surname: "Testowy",
|
||||
phone: "+48 123 456 789"
|
||||
},
|
||||
office: sampleCompanies[0].offices[0],
|
||||
program: [
|
||||
{ description: "Test" },
|
||||
],
|
||||
type: {
|
||||
description: {
|
||||
pl: "Przykładowy typ",
|
||||
en: "Example type"
|
||||
},
|
||||
label: {
|
||||
pl: "Przykładowy",
|
||||
en: "Example"
|
||||
},
|
||||
requiresDeanApproval: true,
|
||||
requiresInsurance: false
|
||||
},
|
||||
state: "declined",
|
||||
changed: moment(),
|
||||
}
|
||||
|
||||
export async function all(edition: Identifiable): Promise<InternshipSubmission[]> {
|
||||
return [
|
||||
sampleInternship,
|
||||
]
|
||||
}
|
||||
|
||||
export async function get(id: Identifier): Promise<InternshipSubmission> {
|
||||
return sampleInternship;
|
||||
}
|
||||
|
||||
export async function approve(internship: OneOrMany<Internship>, comment?: string): Promise<void> {}
|
||||
export async function decline(internship: OneOrMany<Internship>, comment: string): Promise<void> {}
|
47
src/management/edition/internship/common.tsx
Normal file
47
src/management/edition/internship/common.tsx
Normal file
@ -0,0 +1,47 @@
|
||||
import { SubmissionStatus } from "@/state/reducer/submission";
|
||||
import React from "react";
|
||||
import { ClockOutline, NotebookCheckOutline, NotebookEditOutline, NotebookRemoveOutline } from "mdi-material-ui";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Chip } from "@material-ui/core";
|
||||
import { createStyles, makeStyles, Theme } from "@material-ui/core/styles";
|
||||
import { green, orange, red } from "@material-ui/core/colors";
|
||||
import { InternshipSubmission } from "@/management/api/internship";
|
||||
|
||||
const useStateLabelStyles = makeStyles((theme: Theme) => createStyles<SubmissionStatus, {}>({
|
||||
awaiting: {
|
||||
borderColor: orange["800"],
|
||||
color: orange["800"],
|
||||
},
|
||||
declined: {
|
||||
borderColor: red["600"],
|
||||
color: red["600"],
|
||||
},
|
||||
draft: {},
|
||||
accepted: {
|
||||
borderColor: green["600"],
|
||||
color: green["600"]
|
||||
}
|
||||
}))
|
||||
|
||||
export type StateLabelProps = {
|
||||
state: SubmissionStatus;
|
||||
};
|
||||
|
||||
export const StateLabel = ({ state }: StateLabelProps) => {
|
||||
const icons: { [sate in SubmissionStatus]: React.ReactElement } = {
|
||||
accepted: <NotebookCheckOutline/>,
|
||||
awaiting: <ClockOutline/>,
|
||||
declined: <NotebookRemoveOutline/>,
|
||||
draft: <NotebookEditOutline/>
|
||||
}
|
||||
|
||||
const classes = useStateLabelStyles();
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
return <Chip icon={ icons[state] } label={ t(`translation:submission.status.${ state }`) } variant="outlined" className={ classes[state] }/>
|
||||
}
|
||||
|
||||
export const canEdit = (internship: InternshipSubmission) => internship.state != "draft";
|
||||
export const canAccept = (internship: InternshipSubmission) => ["declined", "awaiting"].includes(internship.state);
|
||||
export const canDiscard = (internship: InternshipSubmission) => ["accepted", "awaiting"].includes(internship.state);
|
39
src/management/edition/internship/details.tsx
Normal file
39
src/management/edition/internship/details.tsx
Normal file
@ -0,0 +1,39 @@
|
||||
import React, { useCallback } from "react";
|
||||
import { Link as RouterLink, useRouteMatch } from "react-router-dom";
|
||||
import api from "@/management/api";
|
||||
import { Page } from "@/pages/base";
|
||||
import { EditionManagement, EditionManagementProps } from "@/management/edition/manage";
|
||||
import { Container, Link, Typography } from "@material-ui/core";
|
||||
import { Async } from "@/components/async";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useAsync } from "@/hooks";
|
||||
import { Student } from "@/data";
|
||||
import { route } from "@/routing";
|
||||
import { ProposalPreview } from "@/components/proposalPreview";
|
||||
import { AcceptanceActions } from "@/components/acceptance-action";
|
||||
import { useSpacing } from "@/styles";
|
||||
|
||||
const fullname = (student: Student) => `${student.name} ${student.surname}`;
|
||||
|
||||
export const InternshipDetails = ({ edition }: EditionManagementProps) => {
|
||||
const { params } = useRouteMatch();
|
||||
const internship = useAsync(useCallback(() => api.internship.get(params.internship), [ params.internship ]));
|
||||
const { t } = useTranslation("management");
|
||||
const spacing = useSpacing(2);
|
||||
|
||||
return <Async async={ internship }>
|
||||
{ internship => <Page>
|
||||
<Page.Header maxWidth="lg">
|
||||
<EditionManagement.Breadcrumbs>
|
||||
<Link to={ route("management:edition_internships", { edition: edition.id || "" }) } component={ RouterLink }>{ t("edition.internships.title") }</Link>
|
||||
<Typography color="textPrimary">{ fullname(internship.intern) }</Typography>
|
||||
</EditionManagement.Breadcrumbs>
|
||||
<Page.Title>{ fullname(internship.intern) }</Page.Title>
|
||||
</Page.Header>
|
||||
<Container maxWidth="lg" className={ spacing.vertical }>
|
||||
<ProposalPreview proposal={ internship } />
|
||||
<AcceptanceActions onAccept={ () => {} } onDiscard={ () => {} } label="internship" />
|
||||
</Container>
|
||||
</Page> }
|
||||
</Async>
|
||||
}
|
119
src/management/edition/internship/list.tsx
Normal file
119
src/management/edition/internship/list.tsx
Normal file
@ -0,0 +1,119 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useAsyncState } from "@/hooks";
|
||||
import { useSpacing } from "@/styles";
|
||||
import api from "@/management/api";
|
||||
import { Box, Button, Container, IconButton, Typography } from "@material-ui/core";
|
||||
import MaterialTable, { Column } from "material-table";
|
||||
import { actionsColumn } from "@/management/common/helpers";
|
||||
import { FileFind, Refresh, StickerCheckOutline, StickerRemoveOutline } from "mdi-material-ui";
|
||||
import { Page } from "@/pages/base";
|
||||
import { Actions } from "@/components";
|
||||
import { BulkActions } from "@/management/common/BulkActions";
|
||||
import { Async } from "@/components/async";
|
||||
import { MaterialTableTitle } from "@/management/common/MaterialTableTitle";
|
||||
import { EditionManagement, EditionManagementProps } from "@/management/edition/manage";
|
||||
import { Link as RouterLink } from "react-router-dom";
|
||||
import { route } from "@/routing";
|
||||
import { AcceptanceActions, AcceptSubmissionDialog, DiscardSubmissionDialog } from "@/components/acceptance-action";
|
||||
import { ProposalPreview } from "@/components/proposalPreview";
|
||||
import { InternshipSubmission } from "@/management/api/internship";
|
||||
import { canAccept, canDiscard, StateLabel } from "@/management/edition/internship/common";
|
||||
import { createPortal } from "react-dom";
|
||||
|
||||
const title = "edition.internships.title";
|
||||
|
||||
export const InternshipManagement = ({ edition }: EditionManagementProps) => {
|
||||
const { t } = useTranslation("management");
|
||||
const [result, setInternshipsPromise] = useAsyncState<InternshipSubmission[]>();
|
||||
const [selected, setSelected] = useState<InternshipSubmission[]>([]);
|
||||
const spacing = useSpacing(2);
|
||||
|
||||
const updateInternshipList = () => {
|
||||
setInternshipsPromise(api.internship.all(edition));
|
||||
}
|
||||
|
||||
useEffect(updateInternshipList, []);
|
||||
|
||||
const handleSubmissionAccept = api.internship.approve
|
||||
const handleSubmissionDiscard = api.internship.decline
|
||||
|
||||
const AcceptAction = ({ internship }: { internship: InternshipSubmission }) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return <>
|
||||
<IconButton onClick={ () => setOpen(true) }><StickerCheckOutline /></IconButton>
|
||||
{ createPortal(
|
||||
<AcceptSubmissionDialog onAccept={ comment => handleSubmissionAccept(internship, comment) } label="internship" open={ open } onClose={ () => setOpen(false) }/>,
|
||||
document.getElementById("modals") as Element,
|
||||
) }
|
||||
</>;
|
||||
}
|
||||
|
||||
const DiscardAction = ({ internship }: { internship: InternshipSubmission }) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return <>
|
||||
<IconButton onClick={ () => setOpen(true) }><StickerRemoveOutline /></IconButton>
|
||||
{ createPortal(
|
||||
<DiscardSubmissionDialog onDiscard={ comment => handleSubmissionDiscard(internship, comment) } label="internship" open={ open } onClose={ () => setOpen(false) }/>,
|
||||
document.getElementById("modals") as Element,
|
||||
) }
|
||||
</>;
|
||||
}
|
||||
|
||||
const columns: Column<InternshipSubmission>[] = [
|
||||
{
|
||||
title: t("internship.column.student"),
|
||||
render: internship => <>{internship.intern.name} {internship.intern.surname}</>,
|
||||
},
|
||||
{
|
||||
title: t("internship.column.album"),
|
||||
field: "intern.albumNumber",
|
||||
},
|
||||
{
|
||||
title: t("internship.column.type"),
|
||||
field: "type.label.pl",
|
||||
},
|
||||
{
|
||||
title: t("internship.column.changed"),
|
||||
render: summary => summary.changed.format("yyyy-MM-DD HH:mm")
|
||||
},
|
||||
{
|
||||
title: t("internship.column.status"),
|
||||
render: summary => <StateLabel state={ summary.state } />
|
||||
},
|
||||
actionsColumn(internship => <>
|
||||
{ canAccept(internship) && <AcceptAction internship={ internship } /> }
|
||||
{ canDiscard(internship) && <DiscardAction internship={ internship } /> }
|
||||
<IconButton component={ RouterLink } to={ route("management:edition_internship", { edition: edition.id || "", internship: internship.id || "" }) }><FileFind /></IconButton>
|
||||
</>)
|
||||
];
|
||||
|
||||
return <Page>
|
||||
<Page.Header maxWidth="lg">
|
||||
<EditionManagement.Breadcrumbs>
|
||||
<Typography color="textPrimary">{ t(title) }</Typography>
|
||||
</EditionManagement.Breadcrumbs>
|
||||
<Page.Title>{ t(title) }</Page.Title>
|
||||
</Page.Header>
|
||||
<Container maxWidth="lg" className={ spacing.vertical }>
|
||||
<Actions>
|
||||
<Button onClick={ updateInternshipList } startIcon={ <Refresh /> }>{ t("refresh") }</Button>
|
||||
</Actions>
|
||||
{ selected.length > 0 && <BulkActions>
|
||||
<AcceptanceActions label="internship" onAccept={ comment => handleSubmissionAccept(selected, comment) } onDiscard={ comment => handleSubmissionDiscard(selected, comment) } />
|
||||
</BulkActions> }
|
||||
<Async async={ result } keepValue>{
|
||||
internships => <MaterialTable
|
||||
title={ <MaterialTableTitle result={ result } label={ t(title) }/> }
|
||||
columns={ columns }
|
||||
data={ internships }
|
||||
onSelectionChange={ internships => setSelected(internships) }
|
||||
options={ { selection: true, pageSize: 10 } }
|
||||
detailPanel={ internship => <Box m={ 3 }><ProposalPreview proposal={ internship } /></Box> }
|
||||
/>
|
||||
}</Async>
|
||||
</Container>
|
||||
</Page>
|
||||
}
|
@ -12,6 +12,7 @@ import { createStyles, makeStyles, Theme } from "@material-ui/core/styles";
|
||||
import { useAsync } from "@/hooks";
|
||||
import api from "@/management/api";
|
||||
import { Async } from "@/components/async";
|
||||
import { OneOrMany } from "@/helpers";
|
||||
|
||||
const useSectionStyles = makeStyles((theme: Theme) => createStyles({
|
||||
header: {
|
||||
@ -49,7 +50,7 @@ export const EditionManagement = ({ edition }: EditionManagementProps) => {
|
||||
<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 || "" }) }>
|
||||
<ManagementLink icon={ <BriefcaseAccount/> } route={ route("management:edition_internships", { edition: edition.id || "" }) }>
|
||||
{ t("management:edition.internships.title") }
|
||||
</ManagementLink>
|
||||
<ManagementLink icon={ <FormatPageBreak/> } route={ route("management:edition_report_form", { edition: edition.id || "" }) }>
|
||||
@ -78,7 +79,7 @@ export const EditionManagement = ({ edition }: EditionManagementProps) => {
|
||||
</Page>
|
||||
}
|
||||
|
||||
EditionManagement.Breadcrumbs = ({ children }: { children: React.ReactChild }) => {
|
||||
EditionManagement.Breadcrumbs = ({ children }: { children: OneOrMany<React.ReactChild> }) => {
|
||||
const edition = useContext<Edition | null>(EditionContext);
|
||||
|
||||
return <Management.Breadcrumbs>
|
||||
|
@ -8,6 +8,8 @@ import { InternshipTypeManagement } from "@/management/type/list";
|
||||
import { EditionRouter, EditionManagement } from "@/management/edition/manage";
|
||||
import { EditionReportFields } from "@/management/edition/report/fields/list";
|
||||
import { EditionSettings } from "@/management/edition/settings";
|
||||
import { InternshipManagement } from "@/management/edition/internship/list";
|
||||
import { InternshipDetails } from "@/management/edition/internship/details";
|
||||
|
||||
export const managementRoutes: Route[] = ([
|
||||
{ name: "index", path: "/", content: ManagementIndex, exact: true },
|
||||
@ -15,7 +17,9 @@ export const managementRoutes: Route[] = ([
|
||||
{ name: "edition_router", path: "/editions/:edition", content: EditionRouter },
|
||||
{ name: "edition_report_form", path: "/editions/:edition/report", content: EditionReportFields, tags: ["edition"] },
|
||||
{ name: "edition_settings", path: "/editions/:edition/settings", content: EditionSettings, tags: ["edition"] },
|
||||
{ name: "edition_manage", path: "/editions/:edition", content: EditionManagement, tags: ["edition"] },
|
||||
{ name: "edition_manage", path: "/editions/:edition", content: EditionManagement, tags: ["edition"], exact: true },
|
||||
{ name: "edition_internship", path: "/editions/:edition/internships/:internship", content: InternshipDetails, tags: ["edition"] },
|
||||
{ name: "edition_internships", path: "/editions/:edition/internships", content: InternshipManagement, tags: ["edition"] },
|
||||
{ name: "editions", path: "/editions", content: EditionsManagement },
|
||||
|
||||
{ name: "types", path: "/types", content: InternshipTypeManagement },
|
||||
|
@ -8,7 +8,7 @@ export type PageProps = {
|
||||
} & BoxProps;
|
||||
|
||||
export type PageHeaderProps = {
|
||||
maxWidth?: "sm" | "md" | "lg" | false
|
||||
maxWidth?: "sm" | "md" | "lg" | "xl" | false
|
||||
} & HTMLProps<HTMLDivElement>
|
||||
|
||||
export const Page = ({ title, children, ...props }: PageProps) => {
|
||||
|
@ -13,7 +13,17 @@ actions:
|
||||
edit: Edytuj
|
||||
add: Dodaj
|
||||
|
||||
internship:
|
||||
column:
|
||||
student: Imię i Nazwisko
|
||||
album: Numer Albumu
|
||||
type: Rodzaj praktyki
|
||||
status: Status
|
||||
changed: Data aktualizacji
|
||||
|
||||
edition:
|
||||
internships:
|
||||
title: Zgłoszenia praktyk
|
||||
index:
|
||||
title: "Edycje praktyk"
|
||||
field:
|
||||
|
@ -6842,9 +6842,9 @@ md5.js@^1.3.4:
|
||||
safe-buffer "^5.1.2"
|
||||
|
||||
mdi-material-ui@^6.17.0:
|
||||
version "6.17.0"
|
||||
resolved "https://registry.yarnpkg.com/mdi-material-ui/-/mdi-material-ui-6.17.0.tgz#da69f0b7d7c6fc2255e6007ed8b8ca858c1aede7"
|
||||
integrity sha512-eOprRu31lklPIS1WGe3cM0G/8glKl1WKRvewxjDrgXH2Ryxxg7uQ+uwDUwUEONtLku0p2ZOLzgXUIy2uRy5rLg==
|
||||
version "6.21.0"
|
||||
resolved "https://registry.yarnpkg.com/mdi-material-ui/-/mdi-material-ui-6.21.0.tgz#e052215b0534e6c20abeb7e89c3fd8a421a519fd"
|
||||
integrity sha512-rcO7KmaOhZq4H7vHYpwnjMqHfuJh0PmpEJNssEofWaqoSEABmIwRHUNmdJDPrjrBCTUm4m7tpYexqPOYzkb1Eg==
|
||||
|
||||
mdn-data@2.0.4:
|
||||
version "2.0.4"
|
||||
|
Loading…
Reference in New Issue
Block a user