Skip to content

Commit

Permalink
feat(EventsManager): auto-sync participants to project
Browse files Browse the repository at this point in the history
  • Loading branch information
jakeaturner committed Jul 14, 2023
1 parent 71046af commit 297e078
Show file tree
Hide file tree
Showing 7 changed files with 222 additions and 88 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@ import { useCallback, useEffect, useState } from "react";
import useGlobalError from "../../error/ErrorHooks";
import useDebounce from "../../../hooks/useDebounce";
import axios from "axios";
import { GenericKeyTextValueObj, Project } from "../../../types";
import { GenericKeyTextValueObj, OrgEvent, Project } from "../../../types";

type SyncUsersToProjectModalProps = {
type AutoSyncToProjectModalProps = {
show: boolean;
selectedParticipants: string[];
orgEvent: OrgEvent;
onClose: () => void;
onConfirm: (projectID: string) => void;
};

const SyncUsersToProjectModal: React.FC<SyncUsersToProjectModalProps> = ({
const AutoSyncToProjectModal: React.FC<AutoSyncToProjectModalProps> = ({
show,
selectedParticipants,
orgEvent,
onClose,
onConfirm,
...props
Expand All @@ -31,6 +31,7 @@ const SyncUsersToProjectModal: React.FC<SyncUsersToProjectModalProps> = ({
GenericKeyTextValueObj<string>[]
>([]);
const [selectedProject, setSelectedProject] = useState<string>("");
const [currentProject, setCurrentProject] = useState<Project | null>(null);

const getProjectOpts = async (searchQuery: string) => {
try {
Expand Down Expand Up @@ -70,8 +71,32 @@ const SyncUsersToProjectModal: React.FC<SyncUsersToProjectModalProps> = ({
250
);

async function getCurrentProjectInfo() {
try {
if (!orgEvent.projectSyncID) return;
setLoading(true);
const res = await axios.get("/project/", {
params: { projectID: orgEvent.projectSyncID },
});

if (res.data.err || !res.data.project) {
handleGlobalError(res.data.errMsg);
return;
}

setCurrentProject(res.data.project);
} catch (err) {
handleGlobalError(err);
} finally {
setLoading(false);
}
}

// Clear state when modal closes
useEffect(() => {
if (show) {
getCurrentProjectInfo();
}
if (!show) {
setProjectOpts([]);
setSelectedProject("");
Expand All @@ -91,13 +116,23 @@ const SyncUsersToProjectModal: React.FC<SyncUsersToProjectModalProps> = ({

return (
<Modal size="large" open={show} onClose={onClose} {...props}>
<Modal.Header>Sync All Users to Project</Modal.Header>
<Modal.Header>Configure Auto-Sync Users to Project</Modal.Header>
<Modal.Content scrolling style={{ minHeight: "30vh" }}>
<Form noValidate>
{currentProject && (
<p className="mb-2p">
<strong>Current Auto-Sync Project: </strong>
{currentProject
? `${currentProject.title} (${currentProject.projectID})`
: ""}
</p>
)}
<Form.Group widths="equal">
<Form.Select
search
label="Select Project"
label={
orgEvent.projectSyncID ? "Select New Project" : "Select Project"
}
placeholder="Start typing to search by title..."
options={projectOpts}
onChange={(_e, { value }) => {
Expand All @@ -115,18 +150,23 @@ const SyncUsersToProjectModal: React.FC<SyncUsersToProjectModalProps> = ({
</Form.Group>
</Form>
<p>
<strong>Note:</strong> Only participants with emails that can be matched to a
Conductor account will be added to the project. All participants will
be added with the <strong>Project Member</strong> role. Participants
who were previously synced will not be duplicated.
<strong>Note:</strong> Only participants with emails that can be
matched to a Conductor account will be added to the project. All
participants will be added with the <strong>Project Member</strong>{" "}
role. Existing participants will be synced upon submission of this
form. Future participants will be synced automatically. If a
participant cancels their registration, they <strong>will not</strong>{" "}
automatically be removed from the project.
</p>
<p></p>
</Modal.Content>
<Modal.Actions>
<Button onClick={() => handleConfirm()} color="green">
<Icon name="refresh" />
Confirm Sync
</Button>

{selectedProject && (
<Button onClick={() => handleConfirm()} color="green">
<Icon name="refresh" />
Confirm Sync Settings
</Button>
)}
<Button onClick={onClose} color="grey">
Cancel
</Button>
Expand All @@ -135,4 +175,4 @@ const SyncUsersToProjectModal: React.FC<SyncUsersToProjectModalProps> = ({
);
};

export default SyncUsersToProjectModal;
export default AutoSyncToProjectModal;
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { isEmptyString } from "../../util/HelperFunctions";
import { getLikertResponseText } from "../../util/LikertHelpers";
import PaymentStatusLabel from "./PaymentStatusLabel";
import UnregisterParticipantsModal from "./UnregisterParticipantsModal";
import SyncUsersToProjectModal from "./SyncUsersToProjectModal";
import AutoSyncToProjectModal from "./AutoSyncToProjectModal";

type SelectableParticipant = OrgEventParticipant & {
selected: boolean;
Expand All @@ -33,15 +33,15 @@ type ParticipantsSegmentProps = {
participants: OrgEventParticipant[];
loading: boolean;
canEdit: boolean;
syncSuccess: boolean;
autoSyncSuccess: boolean;
activePage: number;
totalPages: number;
totalItems: number;
itemsPerPage: number;
onDownloadParticipants: () => void;
onChangeActivePage: (page: number) => void;
onUnregisterParticipants: (ids: string[]) => void;
onSyncUsersToProject: (projectID: string) => void;
onConfigureAutoSync: (projectID: string) => void;
};

const ParticipantsSegment: React.FC<ParticipantsSegmentProps> = ({
Expand All @@ -51,23 +51,23 @@ const ParticipantsSegment: React.FC<ParticipantsSegmentProps> = ({
participants,
loading,
canEdit,
syncSuccess,
autoSyncSuccess,
activePage,
totalPages,
totalItems,
itemsPerPage,
onDownloadParticipants,
onChangeActivePage,
onUnregisterParticipants,
onSyncUsersToProject,
onConfigureAutoSync,
...rest
}) => {
// UI
const [tableColumns, setTableColumns] = useState<
{ key: string; text: string }[]
>([]);
const [showUnregisterModal, setShowUnregisterModal] = useState(false);
const [showAddToProjectModal, setShowAddToProjectModal] = useState(false);
const [showSyncProjectModal, setShowSyncProjectModal] = useState(false);
const [selectableParticipants, setSelectableParticipants] = useState<
SelectableParticipant[]
>([]);
Expand Down Expand Up @@ -194,9 +194,9 @@ const ParticipantsSegment: React.FC<ParticipantsSegmentProps> = ({
setSelectableParticipants(arr);
}

function handleSyncUsersToProject(projectID: string) {
onSyncUsersToProject(projectID);
setShowAddToProjectModal(false);
function handleConfigureAutoSync(projectID: string) {
onConfigureAutoSync(projectID);
setShowSyncProjectModal(false);
}

function TableRow({
Expand Down Expand Up @@ -304,10 +304,10 @@ const ParticipantsSegment: React.FC<ParticipantsSegmentProps> = ({
<Segment loading={loading}>
<div className="flex-row-div flex-row-verticalcenter mb-1p">
<div className="left-flex">
{syncSuccess && (
{autoSyncSuccess && (
<Message success>
<Icon name="check" />
<span>Successfully synced users to project</span>
<span>Auto-Sync Configured Successfully</span>
</Message>
)}
</div>
Expand All @@ -322,11 +322,10 @@ const ParticipantsSegment: React.FC<ParticipantsSegmentProps> = ({
</Button>
<Button
color="blue"
disabled={!participants || participants.length === 0}
onClick={() => setShowAddToProjectModal(true)}
onClick={() => setShowSyncProjectModal(true)}
>
<Icon name="refresh" />
<span>Sync All Users to Project</span>
<Icon name="setting" />
<span>Configure Auto-Sync to Project</span>
</Button>
</div>
</div>
Expand Down Expand Up @@ -428,11 +427,11 @@ const ParticipantsSegment: React.FC<ParticipantsSegmentProps> = ({
onClose={() => setShowUnregisterModal(false)}
onConfirm={handleUnregisterParticipants}
/>
<SyncUsersToProjectModal
show={showAddToProjectModal}
selectedParticipants={selectedParticipants.map((p) => p.regID)}
onClose={() => setShowAddToProjectModal(false)}
onConfirm={(projectID) => handleSyncUsersToProject(projectID)}
<AutoSyncToProjectModal
show={showSyncProjectModal}
orgEvent={orgEvent}
onClose={() => setShowSyncProjectModal(false)}
onConfirm={(projectID) => handleConfigureAutoSync(projectID)}
/>
</Grid.Column>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ const ManageEvent = () => {

// Participants Segment
const [loadedParticipants, setLoadedParticipants] = useState<boolean>(true);
const [syncSuccess, setSyncSuccess] = useState<boolean>(false);
const [autoSyncSuccess, setAutoSyncSuccess] = useState<boolean>(false);
const [itemsPerPage, setItemsPerPage] = useState<number>(25);
const [activePage, setActivePage] = useState<number>(1);
const [totalItems, setTotalItems] = useState<number>(0);
Expand Down Expand Up @@ -310,10 +310,22 @@ const ManageEvent = () => {
if (routeParams.mode === "create") {
const createRes = await axios.post("/orgevents", {
...getValues(),
regOpenDate: zonedTimeToUtc(getValues().regOpenDate, getValues().timeZone.value),
regCloseDate: zonedTimeToUtc(getValues().regCloseDate, getValues().timeZone.value),
startDate: zonedTimeToUtc(getValues().startDate, getValues().timeZone.value),
endDate: zonedTimeToUtc(getValues().endDate, getValues().timeZone.value),
regOpenDate: zonedTimeToUtc(
getValues().regOpenDate,
getValues().timeZone.value
),
regCloseDate: zonedTimeToUtc(
getValues().regCloseDate,
getValues().timeZone.value
),
startDate: zonedTimeToUtc(
getValues().startDate,
getValues().timeZone.value
),
endDate: zonedTimeToUtc(
getValues().endDate,
getValues().timeZone.value
),
});
setChangesSaving(false);
if (createRes.data.err) {
Expand All @@ -327,16 +339,25 @@ const ManageEvent = () => {
}

if (routeParams.mode === "edit" && routeParams.eventID) {
const editRes = await axios.patch(
`/orgevents/${routeParams.eventID}`,
{
...getValues(),
regOpenDate: zonedTimeToUtc(getValues().regOpenDate, getValues().timeZone.value),
regCloseDate: zonedTimeToUtc(getValues().regCloseDate, getValues().timeZone.value),
startDate: zonedTimeToUtc(getValues().startDate, getValues().timeZone.value),
endDate: zonedTimeToUtc(getValues().endDate, getValues().timeZone.value),
},
);
const editRes = await axios.patch(`/orgevents/${routeParams.eventID}`, {
...getValues(),
regOpenDate: zonedTimeToUtc(
getValues().regOpenDate,
getValues().timeZone.value
),
regCloseDate: zonedTimeToUtc(
getValues().regCloseDate,
getValues().timeZone.value
),
startDate: zonedTimeToUtc(
getValues().startDate,
getValues().timeZone.value
),
endDate: zonedTimeToUtc(
getValues().endDate,
getValues().timeZone.value
),
});

setChangesSaving(false);
if (editRes.data.err) {
Expand Down Expand Up @@ -432,17 +453,18 @@ const ManageEvent = () => {
}
}

async function handleSyncUsersToProject(projectID: string) {
async function handleConfigureAutoSync(projectID: string) {
try {
if (!projectID || !getValues("eventID")) return;

const res = await axios.put(
`/orgevents/${getValues("eventID")}/sync/${projectID}`
`/orgevents/${getValues("eventID")}/configure-sync/${projectID}`
);
if (res.data.err) {
throw new Error(res.data.errMsg);
}
setSyncSuccess(true);
setAutoSyncSuccess(true);
getOrgEvent(); // Refresh the event data
} catch (err) {
handleGlobalError(err);
return;
Expand Down Expand Up @@ -925,15 +947,15 @@ const ManageEvent = () => {
orgEvent={getValues()}
loading={!loadedParticipants}
canEdit={canEdit}
syncSuccess={syncSuccess}
autoSyncSuccess={autoSyncSuccess}
activePage={activePage}
onDownloadParticipants={handleDownloadParticipants}
onChangeActivePage={(page) => setActivePage(page)}
onUnregisterParticipants={(regIds) =>
handleUnregisterParticipants(regIds)
}
onSyncUsersToProject={(projectID) => {
handleSyncUsersToProject(projectID);
onConfigureAutoSync={(projectID) => {
handleConfigureAutoSync(projectID);
}}
totalItems={totalItems}
totalPages={totalPages}
Expand Down
1 change: 1 addition & 0 deletions client/src/types/OrgEvent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export type OrgEvent = BaseDocument & {
participants: OrgEventParticipant[];
feeWaivers: OrgEventFeeWaiver[];
collectShipping: boolean;
projectSyncID?: string;
createdBy: string;
canceled: boolean;
};
6 changes: 3 additions & 3 deletions server/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -1345,12 +1345,12 @@ router.route('/orgevents/:eventID/feewaivers/:feeWaiverCode').patch(
orgEventsAPI.updateFeeWaiver,
);

router.route('/orgevents/:eventID/sync/:projectID').put(
router.route('/orgevents/:eventID/configure-sync/:projectID').put(
authAPI.verifyRequest,
authAPI.getUserAttributes,
orgEventsAPI.validate('syncUsersToProject'),
orgEventsAPI.validate('configureAutoSync'),
middleware.checkValidationErrors,
orgEventsAPI.syncUsersToProject,
orgEventsAPI.configureAutoSync,
)

router.route('/apiclients/:clientID').get(
Expand Down
Loading

0 comments on commit 297e078

Please sign in to comment.