diff --git a/frontend/src/components/programOccurrence/ProgramOccurrence.js b/frontend/src/components/programOccurrence/ProgramOccurrence.js
index 63c344a2..8e6624e7 100644
--- a/frontend/src/components/programOccurrence/ProgramOccurrence.js
+++ b/frontend/src/components/programOccurrence/ProgramOccurrence.js
@@ -2,10 +2,23 @@ import React, {useEffect, useState} from 'react';
import GenericForm from "../shared/GenericForm";
import {fetchInternalTypeByFormType} from "../../api/internalTypeApi";
import {ProgramAndNeedSatisfierField} from "./ProgramAndNeedSatisfierField";
+import {fetchCharacteristics} from '../../api/characteristicApi';
+import {CapacityField} from '../shared/CapacityField';
export default function ProgramOccurrenceForm() {
const formType = 'programOccurrence';
+ const [characteristics, setCharacteristics] = useState({});
+ useEffect(() => {
+ fetchCharacteristics().then(characteristics => {
+ const data = {};
+ for (const {implementation, name, _id} of characteristics.data) {
+ data[name] = {implementation, _id}
+ }
+ setCharacteristics(data);
+ });
+ }, []);
+
const [internalTypes, setInternalTypes] = useState({});
useEffect(() => {
fetchInternalTypeByFormType(formType).then(({internalTypes}) => {
@@ -16,6 +29,7 @@ export default function ProgramOccurrenceForm() {
setInternalTypes(data);
});
}, []);
+
const handleRenderField = ({required, id, type, implementation, content, _id}, index, fields, handleChange) => {
if (implementation.optionsFromClass?.endsWith("#Program") ) {
// Render Program & Program Occurrence & Need Satisfier
@@ -26,6 +40,11 @@ export default function ProgramOccurrenceForm() {
} else if (implementation.optionsFromClass?.endsWith('#NeedSatisfier')) {
return "";
+ } else if (implementation.label === 'Capacity') {
+ return ;
+ } else if (implementation.label === 'Occupancy') {
+ return ''; // Not editable by the user
}
}
diff --git a/frontend/src/components/programProvision/ProgramAndOccurrenceAndNeedSatisfierField.js b/frontend/src/components/programProvision/ProgramAndOccurrenceAndNeedSatisfierField.js
index c0ed2790..09a5d995 100644
--- a/frontend/src/components/programProvision/ProgramAndOccurrenceAndNeedSatisfierField.js
+++ b/frontend/src/components/programProvision/ProgramAndOccurrenceAndNeedSatisfierField.js
@@ -16,7 +16,9 @@ export function ProgramAndOccurrenceAndNeedSatisfierField({
programOccurrenceFieldId,
needSatisfierFieldId,
handleChange,
- fixedProgramId // full URI of the program which all shown occurrences must be of, if given
+ changeProgramOcc,
+ fixedProgramId, // full URI of the program which all shown occurrences must be of, if given
+ ...others
}) {
const programKey = programFieldId ? `internalType_${programFieldId}` : null;
const programOccKey = `internalType_${programOccurrenceFieldId}`;
@@ -52,6 +54,8 @@ export function ProgramAndOccurrenceAndNeedSatisfierField({
const value = e.target.value;
setSelectedProgramOcc(value);
handleChange(key)(e);
+ if (changeProgramOcc)
+ changeProgramOcc(value);
}
useEffect(() => {
@@ -64,6 +68,8 @@ export function ProgramAndOccurrenceAndNeedSatisfierField({
// unset program occurrence after another program is selected
if (!firstProgram.current) {
setSelectedProgramOcc(null);
+ if (changeProgramOcc)
+ changeProgramOcc(null);
handleChange(programOccKey)(null);
}
setLoadingProgramOcc(false);
@@ -103,7 +109,7 @@ export function ProgramAndOccurrenceAndNeedSatisfierField({
{showProgram ?
+ controlled {...others}/>
: null
}
{showProgramOcc ?
@@ -111,7 +117,7 @@ export function ProgramAndOccurrenceAndNeedSatisfierField({
+ onChange={handleChangeProgramOcc(programOccKey)} controlled {...others}/>
: null
@@ -121,7 +127,7 @@ export function ProgramAndOccurrenceAndNeedSatisfierField({
+ onChange={handleChange(needSatisfierKey)} controlled {...others}/>
: null
diff --git a/frontend/src/components/programRegistration/ProgramOccurrenceAndStatusField.js b/frontend/src/components/programRegistration/ProgramOccurrenceAndStatusField.js
new file mode 100644
index 00000000..ea2907d2
--- /dev/null
+++ b/frontend/src/components/programRegistration/ProgramOccurrenceAndStatusField.js
@@ -0,0 +1,130 @@
+import React, {useEffect, useState} from 'react';
+import {useParams} from 'react-router-dom';
+import {fetchInternalTypeByFormType} from "../../api/internalTypeApi";
+import {ProgramAndOccurrenceAndNeedSatisfierField} from "../programProvision/ProgramAndOccurrenceAndNeedSatisfierField";
+import {fetchCharacteristics} from '../../api/characteristicApi';
+import {getInstancesInClass} from '../../api/dynamicFormApi';
+import {fetchSingleGeneric} from '../../api/genericDataApi';
+import {Box, Fade} from '@mui/material';
+import {Loading} from '../shared';
+import SelectField from '../shared/fields/SelectField';
+
+export default function ProgramOccurrenceAndStatusField({handleChange, fields, serviceOrProgramId, formType}) {
+ const {id} = useParams();
+ const mode = id ? 'edit' : 'new';
+ const [characteristics, setCharacteristics] = useState(null);
+ const [internalTypes, setInternalTypes] = useState(null);
+ const [allStatusOptions, setAllStatusOptions] = useState(null);
+ const [programOccurrenceFieldId, setProgramOccurrenceFieldId] = useState(null);
+ const [programOccKey, setProgramOccKey] = useState(null);
+ const statusFieldKey = `characteristic_${characteristics?.['Registration Status']._id}`;
+ const [selectedProgramOcc, setSelectedProgramOcc] = useState(null);
+ const [statusOptions, setstatusOptions] = useState(null);
+
+ useEffect(() => {
+ fetchCharacteristics().then(characteristics => {
+ const data = {};
+ for (const {implementation, name, _id} of characteristics.data) {
+ data[name] = {implementation, _id}
+ }
+ setCharacteristics(data);
+ });
+ }, []);
+
+ useEffect(() => {
+ fetchInternalTypeByFormType(formType).then(({internalTypes}) => {
+ const data = {}
+ for (const {implementation, name, _id} of internalTypes) {
+ data[name] = {implementation, _id}
+ }
+ setInternalTypes(data);
+ });
+ }, []);
+
+ useEffect(() => {
+ getInstancesInClass(':RegistrationStatus')
+ .then(options => setAllStatusOptions(options));
+ }, []);
+
+ useEffect(() => {
+ setProgramOccurrenceFieldId(internalTypes?.programOccurrenceForProgramRegistration._id);
+ }, [internalTypes]);
+
+ useEffect(() => {
+ setProgramOccKey(`internalType_${programOccurrenceFieldId}`);
+ }, [programOccurrenceFieldId]);
+
+ useEffect(() => {
+ setSelectedProgramOcc(fields[programOccKey]);
+ }, [programOccKey]);
+
+ useEffect(() => {
+ if (!selectedProgramOcc || !allStatusOptions || !characteristics) return;
+ fetchSingleGeneric('programOccurrence', selectedProgramOcc.split('_')[1])
+ .then(occ => {
+ const occupancy = occ.data[`characteristic_${characteristics['Occupancy']._id}`];
+ const capacity = occ.data[`characteristic_${characteristics['Capacity']._id}`];
+ const hasCapacity = (capacity == null) || (occupancy < capacity);
+
+ const registeredUri = Object.keys(allStatusOptions).find(uri => allStatusOptions[uri] === 'Registered');
+ const notRegisteredUri = Object.keys(allStatusOptions).find(uri => allStatusOptions[uri] === 'Not Registered');
+ const waitlistedUri = Object.keys(allStatusOptions).find(uri => allStatusOptions[uri] === 'Waitlisted');
+
+ let options = {};
+ if (mode === 'new') {
+ if (hasCapacity) {
+ options[registeredUri] = 'Register';
+ options[notRegisteredUri] = 'Save without registering';
+ } else {
+ options[waitlistedUri] = 'Waitlist';
+ options[notRegisteredUri] = 'Save without waitlisting';
+ }
+ setstatusOptions(options);
+ } else {
+ fetchSingleGeneric('programRegistration', id)
+ .then(reg => {
+ const status = allStatusOptions[reg.data[`characteristic_${characteristics['Registration Status']._id}`]];
+ if (status === 'Registered') {
+ options[registeredUri] = 'Remain registered';
+ options[notRegisteredUri] = 'Unregister';
+ } else if (status === 'Waitlisted') {
+ options[waitlistedUri] = 'Stay on waitlist';
+ options[notRegisteredUri] = 'Withdraw from waitlist';
+ } else {
+ // If updating a not-registered registration, options are the same as when creating a new registration
+ if (hasCapacity) {
+ options[registeredUri] = 'Register';
+ options[notRegisteredUri] = 'Save without registering';
+ } else {
+ options[waitlistedUri] = 'Waitlist';
+ options[notRegisteredUri] = 'Save without waitlisting';
+ }
+ }
+ setstatusOptions(options);
+ });
+ }
+ });
+ }, [selectedProgramOcc, allStatusOptions, characteristics]);
+
+ if (!characteristics || !internalTypes) {
+ return ;
+ }
+
+ return <>
+ {/* Render Program & Program Occurrence & Need Satisfier */}
+ setSelectedProgramOcc(value)} disabled={mode === 'edit'}/>
+ {!!selectedProgramOcc && !!statusOptions ?
+
+
+
+
+
+ : null
+ }
+ >
+}
\ No newline at end of file
diff --git a/frontend/src/components/programRegistration/ProgramRegistrationForm.js b/frontend/src/components/programRegistration/ProgramRegistrationForm.js
index d320f2e3..68b3e256 100644
--- a/frontend/src/components/programRegistration/ProgramRegistrationForm.js
+++ b/frontend/src/components/programRegistration/ProgramRegistrationForm.js
@@ -1,12 +1,10 @@
import React, {useEffect, useState} from 'react';
import GenericForm from "../shared/GenericForm";
-import { useParams } from "react-router-dom";
import {fetchInternalTypeByFormType} from "../../api/internalTypeApi";
import {ClientAndNeedOccurrenceField} from "../serviceProvision/ClientAndNeedOccurrenceField";
-import {ProgramAndOccurrenceAndNeedSatisfierField} from "../programProvision/ProgramAndOccurrenceAndNeedSatisfierField";
+import ProgramOccurrenceAndStatusField from './ProgramOccurrenceAndStatusField';
export default function ProgramRegistrationForm() {
-
const formType = 'programRegistration';
const [internalTypes, setInternalTypes] = useState({});
@@ -19,6 +17,7 @@ export default function ProgramRegistrationForm() {
setInternalTypes(data);
});
}, []);
+
const handleRenderField = ({required, id, type, implementation, content, serviceOrProgramId}, index, fields, handleChange) => {
console.log(implementation)
if (implementation.optionsFromClass?.endsWith("#Client")) {
@@ -27,19 +26,12 @@ export default function ProgramRegistrationForm() {
clientFieldId={internalTypes.clientForProgramRegistration._id}
needOccFieldId={internalTypes.needOccurrenceForProgramRegistration._id}/>
} else if (implementation.optionsFromClass?.endsWith("#ProgramOccurrence")) {
- const programOccurrenceFieldId = internalTypes.programOccurrenceForProgramRegistration._id;
-
- if (!programOccurrenceFieldId) {
- return ;
- }
-
- // Render Program & Program Occurrence & Need Satisfier
- return
+ return ;
} else if (implementation.optionsFromClass?.endsWith("#NeedOccurrence")) {
return "";
+ } else if (implementation.label === "Registration Status") {
+ return '';
}
}
return (
diff --git a/frontend/src/components/programRegistration/ProgramRegistrations.js b/frontend/src/components/programRegistration/ProgramRegistrations.js
index 86869606..a0b0b988 100644
--- a/frontend/src/components/programRegistration/ProgramRegistrations.js
+++ b/frontend/src/components/programRegistration/ProgramRegistrations.js
@@ -1,8 +1,9 @@
import React from 'react';
import {Link} from "../shared"
-import { GenericPage } from "../shared";
-import { deleteSingleGeneric, fetchMultipleGeneric, fetchSingleGeneric } from "../../api/genericDataApi";
+import {GenericPage} from "../shared";
+import {deleteSingleGeneric, fetchMultipleGeneric} from "../../api/genericDataApi";
import {getAddressCharacteristicId} from "../shared/CharacteristicIds";
+import {getInstancesInClass} from '../../api/dynamicFormApi';
const TYPE = 'programRegistrations';
@@ -16,8 +17,8 @@ const columnsWithoutOptions = [
},
{
label: 'Status',
- body: ({referralStatus}) =>{
- return referralStatus
+ body: ({status}) =>{
+ return status;
}
},
// {
@@ -39,15 +40,14 @@ export default function ProgramRegistrations() {
const fetchData = async () => {
const addressCharacteristicId = await getAddressCharacteristicId();
const programs = (await fetchMultipleGeneric('programRegistration')).data;
+ const statuses = await getInstancesInClass(':RegistrationStatus');
const data = [];
for (const program of programs) {
const programData = {_id: program._id};
if (program.characteristicOccurrences)
for (const occ of program.characteristicOccurrences) {
- if (occ.occurrenceOf?.name === 'Referral Type') {
- programData.referralType = occ.dataStringValue;
- } else if (occ.occurrenceOf?.name === 'Referral Status') {
- programData.referralStatus = occ.objectValue;
+ if (occ.occurrenceOf?.name === 'Registration Status') {
+ programData.status = statuses[occ.dataStringValue];
}
}
if (program.address)
diff --git a/frontend/src/components/programWaitlist/ProgramWaitlist.js b/frontend/src/components/programWaitlist/ProgramWaitlist.js
new file mode 100644
index 00000000..2878303f
--- /dev/null
+++ b/frontend/src/components/programWaitlist/ProgramWaitlist.js
@@ -0,0 +1,23 @@
+import React, {useEffect, useState} from 'react';
+import GenericForm from "../shared/GenericForm";
+import {fetchInternalTypeByFormType} from "../../api/internalTypeApi";
+
+export default function ProgramWaitlistForm() {
+ const formType = 'programWaitlist';
+
+ const [internalTypes, setInternalTypes] = useState({});
+ useEffect(() => {
+ fetchInternalTypeByFormType(formType).then(({internalTypes}) => {
+ const data = {}
+ for (const {implementation, name, _id} of internalTypes) {
+ data[name] = {implementation, _id}
+ }
+ setInternalTypes(data);
+ console.log('programWaitlist internalTypes', data);
+ });
+ }, []);
+
+ return (
+
+ );
+};
\ No newline at end of file
diff --git a/frontend/src/components/programWaitlist/programWaitlists.js b/frontend/src/components/programWaitlist/programWaitlists.js
new file mode 100644
index 00000000..9a62c906
--- /dev/null
+++ b/frontend/src/components/programWaitlist/programWaitlists.js
@@ -0,0 +1,86 @@
+import React from 'react'
+import { Link } from '../shared';
+import { GenericPage } from "../shared";
+import { deleteSingleGeneric, fetchMultipleGeneric, fetchSingleGeneric } from "../../api/genericDataApi";
+import { getInstancesInClass } from "../../api/dynamicFormApi";
+import {getAddressCharacteristicId} from "../shared/CharacteristicIds";
+
+const TYPE = 'programWaitlists';
+
+const columnsWithoutOptions = [
+ {
+ label: 'ID',
+ body: ({_id}) => {
+ console.log(_id);
+ console.log({_id});
+
+ return {_id}
+ }
+ },
+ {
+ label: 'Program Occurrence',
+ body: ({programOccurrence}) => {
+ console.log(programOccurrence);
+ console.log(programOccurrence._id);
+ return programOccurrence._id;
+ }
+ },
+ {
+ label: 'Waitlist Size',
+ body: ({waitlist}) => {
+ if (waitlist) {
+ console.log(waitlist);
+ console.log(waitlist.length);
+ return waitlist.length;
+ } else {
+ return "";
+ }
+ }
+ },
+
+];
+
+export default function ProgramWaitlists() {
+
+ const nameFormatter = programWaitlist => 'Program Waitlist' + programWaitlist._id;
+
+ const linkFormatter = needOccurrence => `/${TYPE}/${programWaitlist._id}`;
+
+ const fetchData = async () => {
+ const addressCharacteristicId = await getAddressCharacteristicId();
+ const programWaitlists = (await fetchMultipleGeneric('programWatilist')).data;
+ const data = [];
+ for (const programWaitlist of programWaitlists) {
+ console.log(programWaitlist._id);
+ console.log(programWaitlist.programOccurrence);
+ console.log(programWaitlist.waitlist);
+
+
+ const programWaitlistData = {
+ _id: programWaitlist._id,
+ programOccurrence: programWaitlist.programOccurrence,
+ waitlist: programWaitlist.waitlist
+ };
+ data.push(programWaitlistData);
+ console.log(data)
+ }
+ return data;
+ }
+
+ const deleteProgramWaitlist = (id) => deleteSingleGeneric('programWaitlist', id);
+
+ return (
+
+ )
+}
diff --git a/frontend/src/components/programWaitlist/visualizeProgramWaitlist.js b/frontend/src/components/programWaitlist/visualizeProgramWaitlist.js
new file mode 100644
index 00000000..c00ee8ea
--- /dev/null
+++ b/frontend/src/components/programWaitlist/visualizeProgramWaitlist.js
@@ -0,0 +1,11 @@
+import React from "react";
+
+import VisualizeGeneric from "../shared/visualizeGeneric";
+
+/**
+ * This function is the frontend for visualizing single service
+ * @returns {JSX.Element}
+ */
+export default function VisualizeProgramWaitlist() {
+ return
+}
\ No newline at end of file
diff --git a/frontend/src/components/serviceOccurrence/ServiceOccurrence.js b/frontend/src/components/serviceOccurrence/ServiceOccurrence.js
index a0da645d..97f27875 100644
--- a/frontend/src/components/serviceOccurrence/ServiceOccurrence.js
+++ b/frontend/src/components/serviceOccurrence/ServiceOccurrence.js
@@ -2,10 +2,23 @@ import React, {useEffect, useState} from 'react';
import GenericForm from "../shared/GenericForm";
import {fetchInternalTypeByFormType} from "../../api/internalTypeApi";
import {ServiceAndNeedSatisfierField} from "./ServiceAndNeedSatisfierField";
+import {fetchCharacteristics} from '../../api/characteristicApi';
+import {CapacityField} from '../shared/CapacityField';
export default function ServiceOccurrenceForm() {
const formType = 'serviceOccurrence';
+ const [characteristics, setCharacteristics] = useState({});
+ useEffect(() => {
+ fetchCharacteristics().then(characteristics => {
+ const data = {};
+ for (const {implementation, name, _id} of characteristics.data) {
+ data[name] = {implementation, _id}
+ }
+ setCharacteristics(data);
+ });
+ }, []);
+
const [internalTypes, setInternalTypes] = useState({});
useEffect(() => {
fetchInternalTypeByFormType(formType).then(({internalTypes}) => {
@@ -17,6 +30,7 @@ export default function ServiceOccurrenceForm() {
console.log('serviceOccurrence internalTypes', data);
});
}, []);
+
const handleRenderField = ({required, id, type, implementation, content, _id}, index, fields, handleChange,step) => {
if (implementation.optionsFromClass?.endsWith("#Service")) {
// Render Service & Service Occurrence & Need Satisfier
@@ -27,6 +41,11 @@ export default function ServiceOccurrenceForm() {
} else if (implementation.optionsFromClass?.endsWith('#NeedSatisfier')) {
return "";
+ } else if (implementation.label === 'Capacity') {
+ return ;
+ } else if (implementation.label === 'Occupancy') {
+ return ''; // Not editable by the user
}
}
diff --git a/frontend/src/components/serviceProvision/ServiceAndOccurrenceAndNeedSatisfierField.js b/frontend/src/components/serviceProvision/ServiceAndOccurrenceAndNeedSatisfierField.js
index 08795715..cb6a705b 100644
--- a/frontend/src/components/serviceProvision/ServiceAndOccurrenceAndNeedSatisfierField.js
+++ b/frontend/src/components/serviceProvision/ServiceAndOccurrenceAndNeedSatisfierField.js
@@ -16,7 +16,9 @@ export function ServiceAndOccurrenceAndNeedSatisfierField({
serviceOccurrenceFieldId,
needSatisfierFieldId,
handleChange,
- fixedServiceId // full URI of the service which all shown occurrences must be of, if given
+ changeServiceOcc,
+ fixedServiceId, // full URI of the service which all shown occurrences must be of, if given
+ ...others
}) {
const serviceKey = serviceFieldId ? `internalType_${serviceFieldId}` : null;
const serviceOccKey = `internalType_${serviceOccurrenceFieldId}`;
@@ -52,6 +54,8 @@ export function ServiceAndOccurrenceAndNeedSatisfierField({
const value = e.target.value;
setSelectedServiceOcc(value);
handleChange(key)(e);
+ if (changeServiceOcc)
+ changeServiceOcc(value);
}
useEffect(() => {
@@ -64,6 +68,8 @@ export function ServiceAndOccurrenceAndNeedSatisfierField({
// unset service occurrence after another service is selected
if (!firstService.current) {
setSelectedServiceOcc(null);
+ if (changeServiceOcc)
+ changeServiceOcc(null);
handleChange(serviceOccKey)(null);
}
setLoadingServiceOcc(false);
@@ -103,7 +109,7 @@ export function ServiceAndOccurrenceAndNeedSatisfierField({
{showService ?
+ controlled {...others}/>
: null
}
{showServiceOcc ?
@@ -111,7 +117,7 @@ export function ServiceAndOccurrenceAndNeedSatisfierField({
+ onChange={handleChangeServiceOcc(serviceOccKey)} controlled {...others}/>
: null
@@ -121,7 +127,7 @@ export function ServiceAndOccurrenceAndNeedSatisfierField({
+ onChange={handleChange(needSatisfierKey)} controlled {...others}/>
: null
diff --git a/frontend/src/components/serviceRegistration/ServiceOccurrenceAndStatusField.js b/frontend/src/components/serviceRegistration/ServiceOccurrenceAndStatusField.js
new file mode 100644
index 00000000..8a6bf3cf
--- /dev/null
+++ b/frontend/src/components/serviceRegistration/ServiceOccurrenceAndStatusField.js
@@ -0,0 +1,130 @@
+import React, {useEffect, useState} from 'react';
+import {useParams} from 'react-router-dom';
+import {fetchInternalTypeByFormType} from "../../api/internalTypeApi";
+import {ServiceAndOccurrenceAndNeedSatisfierField} from "../serviceProvision/ServiceAndOccurrenceAndNeedSatisfierField";
+import {fetchCharacteristics} from '../../api/characteristicApi';
+import {getInstancesInClass} from '../../api/dynamicFormApi';
+import {fetchSingleGeneric} from '../../api/genericDataApi';
+import {Box, Fade} from '@mui/material';
+import {Loading} from '../shared';
+import SelectField from '../shared/fields/SelectField';
+
+export default function ServiceOccurrenceAndStatusField({handleChange, fields, serviceOrProgramId, formType}) {
+ const {id} = useParams();
+ const mode = id ? 'edit' : 'new';
+ const [characteristics, setCharacteristics] = useState(null);
+ const [internalTypes, setInternalTypes] = useState(null);
+ const [allStatusOptions, setAllStatusOptions] = useState(null);
+ const [serviceOccurrenceFieldId, setServiceOccurrenceFieldId] = useState(null);
+ const [serviceOccKey, setServiceOccKey] = useState(null);
+ const statusFieldKey = `characteristic_${characteristics?.['Registration Status']._id}`;
+ const [selectedServiceOcc, setSelectedServiceOcc] = useState(null);
+ const [statusOptions, setstatusOptions] = useState(null);
+
+ useEffect(() => {
+ fetchCharacteristics().then(characteristics => {
+ const data = {};
+ for (const {implementation, name, _id} of characteristics.data) {
+ data[name] = {implementation, _id}
+ }
+ setCharacteristics(data);
+ });
+ }, []);
+
+ useEffect(() => {
+ fetchInternalTypeByFormType(formType).then(({internalTypes}) => {
+ const data = {}
+ for (const {implementation, name, _id} of internalTypes) {
+ data[name] = {implementation, _id}
+ }
+ setInternalTypes(data);
+ });
+ }, []);
+
+ useEffect(() => {
+ getInstancesInClass(':RegistrationStatus')
+ .then(options => setAllStatusOptions(options));
+ }, []);
+
+ useEffect(() => {
+ setServiceOccurrenceFieldId(internalTypes?.serviceOccurrenceForServiceRegistration._id);
+ }, [internalTypes]);
+
+ useEffect(() => {
+ setServiceOccKey(`internalType_${serviceOccurrenceFieldId}`);
+ }, [serviceOccurrenceFieldId]);
+
+ useEffect(() => {
+ setSelectedServiceOcc(fields[serviceOccKey]);
+ }, [serviceOccKey]);
+
+ useEffect(() => {
+ if (!selectedServiceOcc || !allStatusOptions || !characteristics) return;
+ fetchSingleGeneric('serviceOccurrence', selectedServiceOcc.split('_')[1])
+ .then(occ => {
+ const occupancy = occ.data[`characteristic_${characteristics['Occupancy']._id}`];
+ const capacity = occ.data[`characteristic_${characteristics['Capacity']._id}`];
+ const hasCapacity = (capacity == null) || (occupancy < capacity);
+
+ const registeredUri = Object.keys(allStatusOptions).find(uri => allStatusOptions[uri] === 'Registered');
+ const notRegisteredUri = Object.keys(allStatusOptions).find(uri => allStatusOptions[uri] === 'Not Registered');
+ const waitlistedUri = Object.keys(allStatusOptions).find(uri => allStatusOptions[uri] === 'Waitlisted');
+
+ let options = {};
+ if (mode === 'new') {
+ if (hasCapacity) {
+ options[registeredUri] = 'Register';
+ options[notRegisteredUri] = 'Save without registering';
+ } else {
+ options[waitlistedUri] = 'Waitlist';
+ options[notRegisteredUri] = 'Save without waitlisting';
+ }
+ setstatusOptions(options);
+ } else {
+ fetchSingleGeneric('serviceRegistration', id)
+ .then(reg => {
+ const status = allStatusOptions[reg.data[`characteristic_${characteristics['Registration Status']._id}`]];
+ if (status === 'Registered') {
+ options[registeredUri] = 'Remain registered';
+ options[notRegisteredUri] = 'Unregister';
+ } else if (status === 'Waitlisted') {
+ options[waitlistedUri] = 'Stay on waitlist';
+ options[notRegisteredUri] = 'Withdraw from waitlist';
+ } else {
+ // If updating a not-registered registration, options are the same as when creating a new registration
+ if (hasCapacity) {
+ options[registeredUri] = 'Register';
+ options[notRegisteredUri] = 'Save without registering';
+ } else {
+ options[waitlistedUri] = 'Waitlist';
+ options[notRegisteredUri] = 'Save without waitlisting';
+ }
+ }
+ setstatusOptions(options);
+ });
+ }
+ });
+ }, [selectedServiceOcc, allStatusOptions, characteristics]);
+
+ if (!characteristics || !internalTypes) {
+ return ;
+ }
+
+ return <>
+ {/* Render Service & Service Occurrence & Need Satisfier */}
+ setSelectedServiceOcc(value)} disabled={mode === 'edit'}/>
+ {!!selectedServiceOcc && !!statusOptions ?
+
+
+
+
+
+ : null
+ }
+ >
+}
\ No newline at end of file
diff --git a/frontend/src/components/serviceRegistration/ServiceRegistrationForm.js b/frontend/src/components/serviceRegistration/ServiceRegistrationForm.js
index 29867028..ee0cf34a 100644
--- a/frontend/src/components/serviceRegistration/ServiceRegistrationForm.js
+++ b/frontend/src/components/serviceRegistration/ServiceRegistrationForm.js
@@ -1,12 +1,10 @@
import React, {useEffect, useState} from 'react';
import GenericForm from "../shared/GenericForm";
-import { useParams } from "react-router-dom";
import {fetchInternalTypeByFormType} from "../../api/internalTypeApi";
import {ClientAndNeedOccurrenceField} from "../serviceProvision/ClientAndNeedOccurrenceField";
-import {ServiceAndOccurrenceAndNeedSatisfierField} from "../serviceProvision/ServiceAndOccurrenceAndNeedSatisfierField";
+import ServiceOccurrenceAndStatusField from './ServiceOccurrenceAndStatusField';
export default function ServiceRegistrationForm() {
-
const formType = 'serviceRegistration';
const [internalTypes, setInternalTypes] = useState({});
@@ -19,6 +17,7 @@ export default function ServiceRegistrationForm() {
setInternalTypes(data);
});
}, []);
+
const handleRenderField = ({required, id, type, implementation, content, serviceOrProgramId}, index, fields, handleChange) => {
console.log(implementation)
if (implementation.optionsFromClass?.endsWith("#Client")) {
@@ -27,19 +26,12 @@ export default function ServiceRegistrationForm() {
clientFieldId={internalTypes.clientForServiceRegistration._id}
needOccFieldId={internalTypes.needOccurrenceForServiceRegistration._id}/>
} else if (implementation.optionsFromClass?.endsWith("#ServiceOccurrence")) {
- const serviceOccurrenceFieldId = internalTypes.serviceOccurrenceForServiceRegistration._id;
-
- if (!serviceOccurrenceFieldId) {
- return ;
- }
-
- // Render Service & Service Occurrence & Need Satisfier
- return
+ return ;
} else if (implementation.optionsFromClass?.endsWith("#NeedOccurrence")) {
return "";
+ } else if (implementation.label === "Registration Status") {
+ return '';
}
}
return (
diff --git a/frontend/src/components/serviceRegistration/ServiceRegistrations.js b/frontend/src/components/serviceRegistration/ServiceRegistrations.js
index 5e1a4ea8..2bbef1d1 100644
--- a/frontend/src/components/serviceRegistration/ServiceRegistrations.js
+++ b/frontend/src/components/serviceRegistration/ServiceRegistrations.js
@@ -1,8 +1,9 @@
import React from 'react';
import {Link} from "../shared"
-import { GenericPage } from "../shared";
-import { deleteSingleGeneric, fetchMultipleGeneric, fetchSingleGeneric } from "../../api/genericDataApi";
+import {GenericPage} from "../shared";
+import {deleteSingleGeneric, fetchMultipleGeneric} from "../../api/genericDataApi";
import {getAddressCharacteristicId} from "../shared/CharacteristicIds";
+import {getInstancesInClass} from '../../api/dynamicFormApi';
const TYPE = 'serviceRegistrations';
@@ -16,8 +17,8 @@ const columnsWithoutOptions = [
},
{
label: 'Status',
- body: ({referralStatus}) =>{
- return referralStatus
+ body: ({status}) =>{
+ return status;
}
},
// {
@@ -39,15 +40,14 @@ export default function ServiceRegistrations() {
const fetchData = async () => {
const addressCharacteristicId = await getAddressCharacteristicId();
const services = (await fetchMultipleGeneric('serviceRegistration')).data;
+ const statuses = await getInstancesInClass(':RegistrationStatus');
const data = [];
for (const service of services) {
const serviceData = {_id: service._id};
if (service.characteristicOccurrences)
for (const occ of service.characteristicOccurrences) {
- if (occ.occurrenceOf?.name === 'Referral Type') {
- serviceData.referralType = occ.dataStringValue;
- } else if (occ.occurrenceOf?.name === 'Referral Status') {
- serviceData.referralStatus = occ.objectValue;
+ if (occ.occurrenceOf?.name === 'Registration Status') {
+ serviceData.status = statuses[occ.dataStringValue];
}
}
if (service.address)
diff --git a/frontend/src/components/serviceWaitlist/ServiceWaitlist.js b/frontend/src/components/serviceWaitlist/ServiceWaitlist.js
new file mode 100644
index 00000000..17aa6af5
--- /dev/null
+++ b/frontend/src/components/serviceWaitlist/ServiceWaitlist.js
@@ -0,0 +1,23 @@
+import React, {useEffect, useState} from 'react';
+import GenericForm from "../shared/GenericForm";
+import {fetchInternalTypeByFormType} from "../../api/internalTypeApi";
+
+export default function ServiceWaitlistForm() {
+ const formType = 'serviceWaitlist';
+
+ const [internalTypes, setInternalTypes] = useState({});
+ useEffect(() => {
+ fetchInternalTypeByFormType(formType).then(({internalTypes}) => {
+ const data = {}
+ for (const {implementation, name, _id} of internalTypes) {
+ data[name] = {implementation, _id}
+ }
+ setInternalTypes(data);
+ console.log('serviceWaitlist internalTypes', data);
+ });
+ }, []);
+
+ return (
+
+ );
+};
\ No newline at end of file
diff --git a/frontend/src/components/serviceWaitlist/serviceWaitlists.js b/frontend/src/components/serviceWaitlist/serviceWaitlists.js
new file mode 100644
index 00000000..6a8bcf2a
--- /dev/null
+++ b/frontend/src/components/serviceWaitlist/serviceWaitlists.js
@@ -0,0 +1,93 @@
+import React from 'react'
+import { Link } from '../shared';
+import { GenericPage } from "../shared";
+import { deleteSingleGeneric, fetchMultipleGeneric, fetchSingleGeneric } from "../../api/genericDataApi";
+import { getInstancesInClass } from "../../api/dynamicFormApi";
+import {getAddressCharacteristicId} from "../shared/CharacteristicIds";
+
+const TYPE = 'serviceWaitlists';
+
+const columnsWithoutOptions = [
+ {
+ label: 'ID',
+ body: ({_id}) => {
+ console.log(_id);
+ console.log({_id});
+
+ return {_id}
+ }
+ },
+ {
+ label: 'Service Occurrence',
+ body: ({serviceOccurrence}) => {
+ console.log(serviceOccurrence);
+ console.log(serviceOccurrence._id);
+ return serviceOccurrence._id;
+ }
+ },
+ {
+ label: 'Waitlist Size',
+ body: ({waitlist}) => {
+ if (waitlist) {
+ console.log(waitlist);
+ console.log(waitlist.length);
+ return waitlist.length;
+ } else {
+ return "";
+ }
+ }
+ },
+ // {
+ // label: 'Description',
+ // body: ({desc}) => desc
+ // },
+ // {
+ // label: 'Category',
+ // body: ({category}) => category
+ // }
+];
+
+export default function ServiceWaitlists() {
+
+ const nameFormatter = serviceWaitlist => 'Service Waitlist' + serviceWaitlist._id;
+
+ const linkFormatter = needOccurrence => `/${TYPE}/${serviceWaitlist._id}`;
+
+ const fetchData = async () => {
+ const addressCharacteristicId = await getAddressCharacteristicId();
+ const serviceWaitlists = (await fetchMultipleGeneric('serviceWatilist')).data;
+ const data = [];
+ for (const serviceWaitlist of serviceWaitlists) {
+ console.log(serviceWaitlist._id);
+ console.log(serviceWaitlist.serviceOccurrence);
+ console.log(serviceWaitlist.waitlist);
+
+
+ const serviceWaitlistData = {
+ _id: serviceWaitlist._id,
+ serviceOccurrence: serviceWaitlist.serviceOccurrence,
+ waitlist: serviceWaitlist.waitlist
+ };
+ data.push(serviceWaitlistData);
+ console.log(data)
+ }
+ return data;
+ }
+
+ const deleteServiceWaitlist = (id) => deleteSingleGeneric('serviceWaitlist', id);
+
+ return (
+
+ )
+}
diff --git a/frontend/src/components/serviceWaitlist/visualizeServiceWaitlist.js b/frontend/src/components/serviceWaitlist/visualizeServiceWaitlist.js
new file mode 100644
index 00000000..6f1070dd
--- /dev/null
+++ b/frontend/src/components/serviceWaitlist/visualizeServiceWaitlist.js
@@ -0,0 +1,11 @@
+import React from "react";
+
+import VisualizeGeneric from "../shared/visualizeGeneric";
+
+/**
+ * This function is the frontend for visualizing single service
+ * @returns {JSX.Element}
+ */
+export default function VisualizeServiceWaitlist() {
+ return
+}
\ No newline at end of file
diff --git a/frontend/src/components/settings/ManageFormFields.js b/frontend/src/components/settings/ManageFormFields.js
index 3d606240..a5f66e73 100644
--- a/frontend/src/components/settings/ManageFormFields.js
+++ b/frontend/src/components/settings/ManageFormFields.js
@@ -164,6 +164,15 @@ export default function ManageFormFields() {
return;
}
+ if ((formType) === 'client'){
+ //the "48" is the internal type ID for "Needs" and is hardcoded in, there
+ //must be some sort of dynamic way to acquire this, which I will need to look into
+ const hasNeeds = usedInternalTypeIds.some(item => item = fetchInternalTypeByFormType('client'));
+ if (!hasNeeds){
+ setErrors(errors => ({...errors, formName: 'Client Forms must have a needs category'}));
+ return;
+ }
+ }
try {
if (method === 'new') {
diff --git a/frontend/src/components/shared/CapacityField.js b/frontend/src/components/shared/CapacityField.js
new file mode 100644
index 00000000..d9b7966e
--- /dev/null
+++ b/frontend/src/components/shared/CapacityField.js
@@ -0,0 +1,36 @@
+import React from "react";
+import {useState} from "react";
+import SelectField from "../shared/fields/SelectField";
+import {Fade} from "@mui/material";
+import FieldGroup from "./FieldGroup";
+
+export function CapacityField({fields, handleChange, capacityFieldId}) {
+ const capacityFieldKey = `characteristic_${capacityFieldId}`;
+ const [isCapacityLimited, setIsCapacityLimited] = useState(typeof fields[capacityFieldKey] !== 'undefined'
+ ? 'Yes' : 'No');
+
+ const handleChangeCapacity = e => {
+ const value = e.target.value;
+ setIsCapacityLimited(value);
+
+ // if the new isCapacityLimited value is No, unset the inner field
+ if (value === 'No') {
+ handleChange(capacityFieldKey)(null);
+ }
+ }
+
+ return <>
+ handleChangeCapacity(e)}/>
+ {isCapacityLimited === "Yes"
+ ?
+
+
+
+
+ : null
+ }
+ >
+}
diff --git a/frontend/src/components/shared/GenericForm.js b/frontend/src/components/shared/GenericForm.js
index c889ce41..e4450b95 100644
--- a/frontend/src/components/shared/GenericForm.js
+++ b/frontend/src/components/shared/GenericForm.js
@@ -209,7 +209,7 @@ export default function GenericForm({name, mainPage, isProvider, onRenderField})
};
const handleChange = typeAndId => (e) => {
- form.fields[typeAndId] = e?.target ? e?.target?.value ?? undefined : e;
+ form.fields[typeAndId] = e?.target ? e?.target?.value ?? null : e;
};
const getStepContent = stepIdx => {
diff --git a/frontend/src/components/shared/fields/GeneralField.js b/frontend/src/components/shared/fields/GeneralField.js
index 7a18d83f..e81e9112 100644
--- a/frontend/src/components/shared/fields/GeneralField.js
+++ b/frontend/src/components/shared/fields/GeneralField.js
@@ -35,12 +35,15 @@ export default function GeneralField({type, onChange, value: defaultValue, ...pr
else
return null;
}
- return defaultValue || '';
+ return defaultValue ?? '';
});
const handleChange = useCallback(e => {
- const val = e.target.value;
+ let val = e.target.value;
setValue(val);
+ if (type === 'number' && val === '') {
+ val = null;
+ }
onChange({target: {value: val}});
}, [onChange, type]);
diff --git a/frontend/src/constants/provider_fields.js b/frontend/src/constants/provider_fields.js
index 14f50d3e..f0f4bb48 100644
--- a/frontend/src/constants/provider_fields.js
+++ b/frontend/src/constants/provider_fields.js
@@ -20,6 +20,8 @@ export const allForms = {
serviceOccurrence: 'Service Occurrence',
serviceRegistration: 'Service Registration',
serviceProvision: 'Service Provision',
+ serviceWaitlist: 'Service Waitlist',
+ programWaitlist: 'Program Waitlist',
programOccurrence: 'Program Occurrence',
programRegistration: 'Program Registration',
programProvision: 'Program Provision',
diff --git a/frontend/src/routes.js b/frontend/src/routes.js
index f2035445..549da5ba 100644
--- a/frontend/src/routes.js
+++ b/frontend/src/routes.js
@@ -25,6 +25,10 @@ import AdminRoute from './components/routes/AdminRoute';
import Notifications from './components/Notifications';
import Providers from './components/Providers';
import ProviderForm from './components/providers/ProviderForm2';
+import ServiceWaitlists from './components/ServiceWaitlists';
+import ServiceWaitlistForm from './components/serviceWaitlist/ServiceWaitlist';
+import ProgramWaitlists from './components/ProgramWaitlists';
+import ProgramWaitlistForm from './components/programWaitlist/ProgramWaitlist';
import Services from './components/Services';
import Programs from './components/Programs';
// import ServiceForm from './components/services/ServiceForm'
@@ -52,6 +56,8 @@ import VisualizeService from "./components/services/visualizeService";
import VisualizeProgram from "./components/programs/visualizeProgram";
import VisualizeServiceOccurrence from "./components/serviceOccurrence/visualizeServiceOccurrence";
import VisualizeProgramOccurrence from "./components/programOccurrence/visualizeProgramOccurrence";
+import VisualizeServiceWaitlist from "./components/serviceWaitlist/visualizeServiceWaitlist";
+import VisualizeProgramWaitlist from "./components/programWaitlist/visualizeProgramWaitlist";
import VisualizeServiceRegistration from "./components/serviceRegistration/visualizeServiceRegistration";
import VisualizeProgramRegistration from "./components/programRegistration/visualizeProgramRegistration";
import VisualizeServiceProvision from "./components/serviceProvision/visualizeServiceProvision";
@@ -177,6 +183,16 @@ const routes = (
}/>
}/>
}/>
+
+ }/>
+ }/>
+ }/>
+ }/>
+
+ }/>
+ }/>
+ }/>
+ }/>
}/>
}/>