More stuff
This commit is contained in:
parent
e31d89b688
commit
dffc279b4a
@ -1,5 +1,5 @@
|
|||||||
import { Address, Company, Identifiable, Internship, Mentor, Office } from "@/data";
|
import { Address, Company, Identifiable, Internship, Mentor, Office, Student } from "@/data";
|
||||||
import { momentSerializationTransformer, OneWayTransformer } from "@/serialization";
|
import { momentSerializationTransformer, OneWayTransformer, Transformer } from "@/serialization";
|
||||||
import { Nullable } from "@/helpers";
|
import { Nullable } from "@/helpers";
|
||||||
import { MentorDTO, mentorDtoTransformer } from "@/api/dto/mentor";
|
import { MentorDTO, mentorDtoTransformer } from "@/api/dto/mentor";
|
||||||
import { InternshipTypeDTO, internshipTypeDtoTransformer } from "@/api/dto/type";
|
import { InternshipTypeDTO, internshipTypeDtoTransformer } from "@/api/dto/type";
|
||||||
@ -7,6 +7,8 @@ import { Moment } from "moment-timezone";
|
|||||||
import { sampleStudent } from "@/provider/dummy";
|
import { sampleStudent } from "@/provider/dummy";
|
||||||
import { UploadType } from "@/api/upload";
|
import { UploadType } from "@/api/upload";
|
||||||
import { ProgramEntryDTO, programEntryDtoTransformer } from "@/api/dto/edition";
|
import { ProgramEntryDTO, programEntryDtoTransformer } from "@/api/dto/edition";
|
||||||
|
import { StudentDTO } from "@/api/dto/student";
|
||||||
|
import { SubmissionStatus } from "@/state/reducer/submission";
|
||||||
|
|
||||||
export enum SubmissionState {
|
export enum SubmissionState {
|
||||||
Draft = "Draft",
|
Draft = "Draft",
|
||||||
@ -16,6 +18,35 @@ export enum SubmissionState {
|
|||||||
Archival = "Archival",
|
Archival = "Archival",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const submissionStateDtoTransformer: Transformer<SubmissionState, SubmissionStatus> = {
|
||||||
|
reverseTransform(subject: SubmissionStatus, context: undefined): SubmissionState {
|
||||||
|
switch (subject) {
|
||||||
|
case "draft":
|
||||||
|
return SubmissionState.Draft;
|
||||||
|
case "awaiting":
|
||||||
|
return SubmissionState.Submitted;
|
||||||
|
case "accepted":
|
||||||
|
return SubmissionState.Accepted;
|
||||||
|
case "declined":
|
||||||
|
return SubmissionState.Rejected;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
transform(subject: SubmissionState, context: undefined): SubmissionStatus {
|
||||||
|
switch (subject) {
|
||||||
|
case SubmissionState.Draft:
|
||||||
|
return "draft";
|
||||||
|
case SubmissionState.Submitted:
|
||||||
|
return "awaiting";
|
||||||
|
case SubmissionState.Accepted:
|
||||||
|
return "accepted";
|
||||||
|
case SubmissionState.Rejected:
|
||||||
|
return "declined";
|
||||||
|
case SubmissionState.Archival:
|
||||||
|
return "declined";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export interface NewBranchOffice extends Address {
|
export interface NewBranchOffice extends Address {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,6 +81,7 @@ export interface InternshipRegistrationDTO extends Identifiable {
|
|||||||
branchAddress: Office,
|
branchAddress: Office,
|
||||||
declaredHours: number,
|
declaredHours: number,
|
||||||
subjects: { subject: ProgramEntryDTO }[],
|
subjects: { subject: ProgramEntryDTO }[],
|
||||||
|
submissionDate: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface InternshipDocument extends Identifiable {
|
export interface InternshipDocument extends Identifiable {
|
||||||
@ -60,9 +92,10 @@ export interface InternshipDocument extends Identifiable {
|
|||||||
|
|
||||||
const reference = (subject: Identifiable | null): Identifiable | null => subject && { id: subject.id };
|
const reference = (subject: Identifiable | null): Identifiable | null => subject && { id: subject.id };
|
||||||
|
|
||||||
export interface InternshipInfoDTO {
|
export interface InternshipInfoDTO extends Identifiable {
|
||||||
internshipRegistration: InternshipRegistrationDTO;
|
internshipRegistration: InternshipRegistrationDTO;
|
||||||
documentation: InternshipDocument[],
|
documentation: InternshipDocument[],
|
||||||
|
student: StudentDTO,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const internshipRegistrationUpdateTransformer: OneWayTransformer<Nullable<Internship>, Nullable<InternshipRegistrationUpdate>> = {
|
export const internshipRegistrationUpdateTransformer: OneWayTransformer<Nullable<Internship>, Nullable<InternshipRegistrationUpdate>> = {
|
||||||
|
@ -24,7 +24,7 @@ export const ProposalPreview = ({ proposal }: ProposalPreviewProps) => {
|
|||||||
<StudentPreview student={ proposal.intern } />
|
<StudentPreview student={ proposal.intern } />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Section>
|
{ proposal.company && proposal.office && <Section>
|
||||||
<Label>{ t('internship.sections.place') }</Label>
|
<Label>{ t('internship.sections.place') }</Label>
|
||||||
<Typography className="proposal__primary">
|
<Typography className="proposal__primary">
|
||||||
{ proposal.company.name }
|
{ proposal.company.name }
|
||||||
@ -36,12 +36,12 @@ export const ProposalPreview = ({ proposal }: ProposalPreviewProps) => {
|
|||||||
<Label>{ t('internship.office') }</Label>
|
<Label>{ t('internship.office') }</Label>
|
||||||
<Typography className="proposal__primary">{ t('internship.address.city', proposal.office.address) }</Typography>
|
<Typography className="proposal__primary">{ t('internship.address.city', proposal.office.address) }</Typography>
|
||||||
<Typography className="proposal__secondary">{ t('internship.address.street', proposal.office.address) }</Typography>
|
<Typography className="proposal__secondary">{ t('internship.address.street', proposal.office.address) }</Typography>
|
||||||
</Section>
|
</Section> }
|
||||||
|
|
||||||
<Section>
|
{ proposal.type && <Section>
|
||||||
<Label>{ t('internship.sections.kind') }</Label>
|
<Label>{ t('internship.sections.kind') }</Label>
|
||||||
<Typography className="proposal__primary">{ proposal.type.label.pl }</Typography>
|
<Typography className="proposal__primary">{ proposal.type.label.pl }</Typography>
|
||||||
</Section>
|
</Section> }
|
||||||
|
|
||||||
<Section>
|
<Section>
|
||||||
<Label>{ t('internship.sections.program') }</Label>
|
<Label>{ t('internship.sections.program') }</Label>
|
||||||
|
@ -1,12 +1,20 @@
|
|||||||
import { Identifiable, Identifier, Internship } from "@/data";
|
import { Identifiable, Identifier, Internship } from "@/data";
|
||||||
import { sampleCompanies, sampleStudent } from "@/provider/dummy";
|
import { sampleCompanies, sampleStudent } from "@/provider/dummy";
|
||||||
import moment, { Moment } from "moment-timezone";
|
import moment, { Moment } from "moment-timezone";
|
||||||
import { OneOrMany } from "@/helpers";
|
import { encapsulate, Nullable, OneOrMany } from "@/helpers";
|
||||||
import { SubmissionState, SubmissionStatus } from "@/state/reducer/submission";
|
import { SubmissionStatus } from "@/state/reducer/submission";
|
||||||
|
import { axios } from "@/api";
|
||||||
|
import { prepare, query } from "@/routing";
|
||||||
|
import { InternshipInfoDTO, submissionStateDtoTransformer } from "@/api/dto/internship-registration";
|
||||||
|
import { Transformer } from "@/serialization";
|
||||||
|
import { mentorDtoTransformer } from "@/api/dto/mentor";
|
||||||
|
import { internshipTypeDtoTransformer } from "@/api/dto/type";
|
||||||
|
import { studentDtoTransfer } from "@/api/dto/student";
|
||||||
|
import { programEntryDtoTransformer } from "@/api/dto/edition";
|
||||||
|
|
||||||
export type InternshipSubmission = Internship & {
|
export type InternshipSubmission = Nullable<Internship> & {
|
||||||
state: SubmissionStatus,
|
state: SubmissionStatus,
|
||||||
changed: Moment
|
changed: Moment | null,
|
||||||
}
|
}
|
||||||
|
|
||||||
const sampleInternship: InternshipSubmission = {
|
const sampleInternship: InternshipSubmission = {
|
||||||
@ -44,15 +52,55 @@ const sampleInternship: InternshipSubmission = {
|
|||||||
changed: moment(),
|
changed: moment(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const INTERNSHIP_MANAGEMENT_INDEX_ENDPOINT = "/management/internship";
|
||||||
|
const INTERNSHIP_MANAGEMENT_ENDPOINT = "/management/internship/:id";
|
||||||
|
const INTERNSHIP_ACCEPT_ENDPOINT = "/management/internship/accept/:id";
|
||||||
|
const INTERNSHIP_REJECT_ENDPOINT = "/management/internship/reject/:id";
|
||||||
|
|
||||||
|
const internshipInfoDtoTransformer: Transformer<InternshipInfoDTO, InternshipSubmission> = {
|
||||||
|
transform(subject: InternshipInfoDTO, context?: never): InternshipSubmission {
|
||||||
|
return {
|
||||||
|
changed: moment(subject.internshipRegistration.submissionDate),
|
||||||
|
company: subject.internshipRegistration.company,
|
||||||
|
startDate: moment(subject.internshipRegistration.start),
|
||||||
|
endDate: moment(subject.internshipRegistration.end),
|
||||||
|
hours: subject.internshipRegistration.declaredHours,
|
||||||
|
id: subject.id,
|
||||||
|
intern: subject.student && studentDtoTransfer.transform(subject.student),
|
||||||
|
isAccepted: false,
|
||||||
|
lengthInWeeks: 0,
|
||||||
|
mentor: subject.internshipRegistration.mentor && mentorDtoTransformer.transform(subject.internshipRegistration.mentor),
|
||||||
|
office: subject.internshipRegistration.branchAddress,
|
||||||
|
program: (subject.internshipRegistration.subjects || []).map(subject => programEntryDtoTransformer.transform(subject.subject)),
|
||||||
|
state: submissionStateDtoTransformer.transform(subject.internshipRegistration.state),
|
||||||
|
type: subject.internshipRegistration.type && internshipTypeDtoTransformer.transform(subject.internshipRegistration.type),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
reverseTransform(subject: InternshipSubmission, context: undefined): InternshipInfoDTO {
|
||||||
|
return {} as any;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
export async function all(edition: Identifiable): Promise<InternshipSubmission[]> {
|
export async function all(edition: Identifiable): Promise<InternshipSubmission[]> {
|
||||||
return [
|
const result = await axios.get<InternshipInfoDTO[]>(query(INTERNSHIP_MANAGEMENT_INDEX_ENDPOINT, { EditionId: edition.id || "" }));
|
||||||
sampleInternship,
|
|
||||||
]
|
return result.data.map(result => internshipInfoDtoTransformer.transform(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function get(id: Identifier): Promise<InternshipSubmission> {
|
export async function get(id: Identifier): Promise<InternshipSubmission> {
|
||||||
return sampleInternship;
|
const result = await axios.get<InternshipInfoDTO>(prepare(INTERNSHIP_MANAGEMENT_ENDPOINT, { id }))
|
||||||
|
|
||||||
|
return internshipInfoDtoTransformer.transform(result.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function approve(internship: OneOrMany<Internship>, comment?: string): Promise<void> {}
|
export async function accept(internship: OneOrMany<Internship>, comment?: string): Promise<void> {
|
||||||
export async function decline(internship: OneOrMany<Internship>, comment: string): Promise<void> {}
|
const internships = encapsulate(internship)
|
||||||
|
|
||||||
|
await Promise.all(internships.map(internship => axios.put(prepare(INTERNSHIP_ACCEPT_ENDPOINT, { id: internship.id || ""}))))
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function discard(internship: OneOrMany<Internship>, comment: string): Promise<void> {
|
||||||
|
const internships = encapsulate(internship)
|
||||||
|
|
||||||
|
await Promise.all(internships.map(internship => axios.put(prepare(INTERNSHIP_REJECT_ENDPOINT, { id: internship.id || ""}))))
|
||||||
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useCallback, useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useAsyncState } from "@/hooks";
|
import { useAsync, useAsyncState } from "@/hooks";
|
||||||
import { useSpacing } from "@/styles";
|
import { useSpacing } from "@/styles";
|
||||||
import api from "@/management/api";
|
import api from "@/management/api";
|
||||||
import { Box, Button, Container, IconButton, Typography } from "@material-ui/core";
|
import { Box, Button, Container, IconButton, Typography } from "@material-ui/core";
|
||||||
@ -15,11 +15,12 @@ import { MaterialTableTitle } from "@/management/common/MaterialTableTitle";
|
|||||||
import { EditionManagement, EditionManagementProps } from "@/management/edition/manage";
|
import { EditionManagement, EditionManagementProps } from "@/management/edition/manage";
|
||||||
import { Link as RouterLink } from "react-router-dom";
|
import { Link as RouterLink } from "react-router-dom";
|
||||||
import { route } from "@/routing";
|
import { route } from "@/routing";
|
||||||
import { AcceptanceActions, AcceptSubmissionDialog, DiscardSubmissionDialog } from "@/components/acceptance-action";
|
import { AcceptSubmissionDialog, DiscardSubmissionDialog } from "@/components/acceptance-action";
|
||||||
import { ProposalPreview } from "@/components/proposalPreview";
|
import { ProposalPreview } from "@/components/proposalPreview";
|
||||||
import { InternshipSubmission } from "@/management/api/internship";
|
import { InternshipSubmission } from "@/management/api/internship";
|
||||||
import { canAccept, canDiscard, StateLabel } from "@/management/edition/internship/common";
|
import { canAccept, canDiscard, StateLabel } from "@/management/edition/internship/common";
|
||||||
import { createPortal } from "react-dom";
|
import { createPortal } from "react-dom";
|
||||||
|
import { Internship } from "@/data";
|
||||||
|
|
||||||
const title = "edition.internships.title";
|
const title = "edition.internships.title";
|
||||||
|
|
||||||
@ -35,16 +36,19 @@ export const InternshipManagement = ({ edition }: EditionManagementProps) => {
|
|||||||
|
|
||||||
useEffect(updateInternshipList, []);
|
useEffect(updateInternshipList, []);
|
||||||
|
|
||||||
const handleSubmissionAccept = api.internship.approve
|
|
||||||
const handleSubmissionDiscard = api.internship.decline
|
|
||||||
|
|
||||||
const AcceptAction = ({ internship }: { internship: InternshipSubmission }) => {
|
const AcceptAction = ({ internship }: { internship: InternshipSubmission }) => {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
|
const handleSubmissionAccept = async (comment?: string) => {
|
||||||
|
setOpen(false);
|
||||||
|
await api.internship.accept(internship as Internship, comment);
|
||||||
|
updateInternshipList();
|
||||||
|
}
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
<IconButton onClick={ () => setOpen(true) }><StickerCheckOutline /></IconButton>
|
<IconButton onClick={ () => setOpen(true) }><StickerCheckOutline /></IconButton>
|
||||||
{ createPortal(
|
{ createPortal(
|
||||||
<AcceptSubmissionDialog onAccept={ comment => handleSubmissionAccept(internship, comment) } label="internship" open={ open } onClose={ () => setOpen(false) }/>,
|
<AcceptSubmissionDialog onAccept={ handleSubmissionAccept } label="internship" open={ open } onClose={ () => setOpen(false) }/>,
|
||||||
document.getElementById("modals") as Element,
|
document.getElementById("modals") as Element,
|
||||||
) }
|
) }
|
||||||
</>;
|
</>;
|
||||||
@ -53,19 +57,30 @@ export const InternshipManagement = ({ edition }: EditionManagementProps) => {
|
|||||||
const DiscardAction = ({ internship }: { internship: InternshipSubmission }) => {
|
const DiscardAction = ({ internship }: { internship: InternshipSubmission }) => {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
|
const handleSubmissionDiscard = async (comment: string) => {
|
||||||
|
setOpen(false);
|
||||||
|
await api.internship.discard(internship as Internship, comment);
|
||||||
|
updateInternshipList();
|
||||||
|
}
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
<IconButton onClick={ () => setOpen(true) }><StickerRemoveOutline /></IconButton>
|
<IconButton onClick={ () => setOpen(true) }><StickerRemoveOutline /></IconButton>
|
||||||
{ createPortal(
|
{ createPortal(
|
||||||
<DiscardSubmissionDialog onDiscard={ comment => handleSubmissionDiscard(internship, comment) } label="internship" open={ open } onClose={ () => setOpen(false) }/>,
|
<DiscardSubmissionDialog onDiscard={ handleSubmissionDiscard } label="internship" open={ open } onClose={ () => setOpen(false) }/>,
|
||||||
document.getElementById("modals") as Element,
|
document.getElementById("modals") as Element,
|
||||||
) }
|
) }
|
||||||
</>;
|
</>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const InternshipDetails = ({ summary }: { summary: InternshipSubmission }) => {
|
||||||
|
const internship = useAsync(useCallback(() => api.internship.get(summary.id || ""), [ summary.id ]))
|
||||||
|
return <Box m={ 3 }><Async async={ internship }>{ internship => <ProposalPreview proposal={ internship as Internship } /> }</Async> </Box>
|
||||||
|
}
|
||||||
|
|
||||||
const columns: Column<InternshipSubmission>[] = [
|
const columns: Column<InternshipSubmission>[] = [
|
||||||
{
|
{
|
||||||
title: t("internship.column.student"),
|
title: t("internship.column.student"),
|
||||||
render: internship => <>{internship.intern.name} {internship.intern.surname}</>,
|
render: internship => <>{internship.intern?.name} {internship.intern?.surname}</>,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("internship.column.album"),
|
title: t("internship.column.album"),
|
||||||
@ -77,7 +92,7 @@ export const InternshipManagement = ({ edition }: EditionManagementProps) => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("internship.column.changed"),
|
title: t("internship.column.changed"),
|
||||||
render: summary => summary.changed.format("yyyy-MM-DD HH:mm")
|
render: summary => summary.changed?.format("yyyy-MM-DD")
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("internship.column.status"),
|
title: t("internship.column.status"),
|
||||||
@ -102,7 +117,7 @@ export const InternshipManagement = ({ edition }: EditionManagementProps) => {
|
|||||||
<Button onClick={ updateInternshipList } startIcon={ <Refresh /> }>{ t("refresh") }</Button>
|
<Button onClick={ updateInternshipList } startIcon={ <Refresh /> }>{ t("refresh") }</Button>
|
||||||
</Actions>
|
</Actions>
|
||||||
{ selected.length > 0 && <BulkActions>
|
{ selected.length > 0 && <BulkActions>
|
||||||
<AcceptanceActions label="internship" onAccept={ comment => handleSubmissionAccept(selected, comment) } onDiscard={ comment => handleSubmissionDiscard(selected, comment) } />
|
|
||||||
</BulkActions> }
|
</BulkActions> }
|
||||||
<Async async={ result } keepValue>{
|
<Async async={ result } keepValue>{
|
||||||
internships => <MaterialTable
|
internships => <MaterialTable
|
||||||
@ -111,7 +126,7 @@ export const InternshipManagement = ({ edition }: EditionManagementProps) => {
|
|||||||
data={ internships }
|
data={ internships }
|
||||||
onSelectionChange={ internships => setSelected(internships) }
|
onSelectionChange={ internships => setSelected(internships) }
|
||||||
options={ { selection: true, pageSize: 10 } }
|
options={ { selection: true, pageSize: 10 } }
|
||||||
detailPanel={ internship => <Box m={ 3 }><ProposalPreview proposal={ internship } /></Box> }
|
detailPanel={ summary => <InternshipDetails summary={ summary } /> }
|
||||||
/>
|
/>
|
||||||
}</Async>
|
}</Async>
|
||||||
</Container>
|
</Container>
|
||||||
|
Loading…
Reference in New Issue
Block a user