diff --git a/web/src/app/schedules/calendar-subscribe/CalendarSubscribeCreateDialog.js b/web/src/app/schedules/calendar-subscribe/CalendarSubscribeCreateDialog.tsx similarity index 56% rename from web/src/app/schedules/calendar-subscribe/CalendarSubscribeCreateDialog.js rename to web/src/app/schedules/calendar-subscribe/CalendarSubscribeCreateDialog.tsx index 0ab6cfb8a9..207c38f795 100644 --- a/web/src/app/schedules/calendar-subscribe/CalendarSubscribeCreateDialog.js +++ b/web/src/app/schedules/calendar-subscribe/CalendarSubscribeCreateDialog.tsx @@ -1,13 +1,12 @@ -import React, { useState } from 'react' -import { useMutation, gql } from '@apollo/client' -import { PropTypes as p } from 'prop-types' +import React, { ReactNode, useState } from 'react' +import { useMutation, gql } from 'urql' import FormDialog from '../../dialogs/FormDialog' import CalendarSubscribeForm from './CalendarSubscribeForm' import { fieldErrors, nonFieldErrors } from '../../util/errutil' import { Typography } from '@mui/material' -import makeStyles from '@mui/styles/makeStyles' import { CheckCircleOutline as SuccessIcon } from '@mui/icons-material' import CalenderSuccessForm from './CalendarSuccessForm' +import { UserCalendarSubscription } from '../../../schema' const mutation = gql` mutation ($input: CreateUserCalendarSubscriptionInput!) { @@ -18,20 +17,13 @@ const mutation = gql` } ` -const useStyles = makeStyles((theme) => ({ - successIcon: { - marginRight: theme.spacing(1), - }, - successTitle: { - display: 'flex', - alignItems: 'center', - }, -})) - const SUBTITLE = 'Create a unique iCalendar subscription URL that can be used in your preferred calendar application.' -export function getSubtitle(isComplete, defaultSubtitle) { +export function getSubtitle( + isComplete: boolean, + defaultSubtitle: string, +): string { const completedSubtitle = 'Your subscription has been created! You can' + ' manage your subscriptions from your profile at any time.' @@ -39,7 +31,11 @@ export function getSubtitle(isComplete, defaultSubtitle) { return isComplete ? completedSubtitle : defaultSubtitle } -export function getForm(isComplete, defaultForm, data) { +export function getForm( + isComplete: boolean, + defaultForm: ReactNode, + data: { createUserCalendarSubscription: UserCalendarSubscription }, +): ReactNode { return isComplete ? ( ) : ( @@ -47,9 +43,14 @@ export function getForm(isComplete, defaultForm, data) { ) } -export default function CalendarSubscribeCreateDialog(props) { - const classes = useStyles() +interface CalendarSubscribeCreateDialogProps { + onClose: () => void + scheduleID?: string +} +export default function CalendarSubscribeCreateDialog( + props: CalendarSubscribeCreateDialogProps, +): ReactNode { const [value, setValue] = useState({ name: '', scheduleID: props.scheduleID || null, @@ -57,24 +58,14 @@ export default function CalendarSubscribeCreateDialog(props) { fullSchedule: false, }) - const [createSubscription, status] = useMutation(mutation, { - variables: { - input: { - scheduleID: value.scheduleID, - name: value.name, - reminderMinutes: [0], // default reminder at shift start time - disabled: false, - fullSchedule: value.fullSchedule, - }, - }, - }) + const [status, commit] = useMutation(mutation) const isComplete = Boolean(status?.data?.createUserCalendarSubscription?.url) const form = ( - +
+ theme.spacing(1) }} /> Success!
) : ( @@ -96,15 +92,26 @@ export default function CalendarSubscribeCreateDialog(props) { subTitle={getSubtitle(isComplete, SUBTITLE)} onClose={props.onClose} alert={isComplete} + loading={status.fetching} errors={nonFieldErrors(status.error)} primaryActionLabel={isComplete ? 'Done' : null} - onSubmit={() => (isComplete ? props.onClose() : createSubscription())} + onSubmit={() => + isComplete + ? props.onClose() + : commit( + { + input: { + scheduleID: value.scheduleID, + name: value.name, + reminderMinutes: [0], // default reminder at shift start time + disabled: false, + fullSchedule: value.fullSchedule, + }, + }, + { additionalTypenames: ['User'] }, + ) + } form={getForm(isComplete, form, status.data)} /> ) } - -CalendarSubscribeCreateDialog.propTypes = { - onClose: p.func.isRequired, - scheduleID: p.string, -} diff --git a/web/src/app/schedules/calendar-subscribe/CalendarSubscribeDeleteDialog.js b/web/src/app/schedules/calendar-subscribe/CalendarSubscribeDeleteDialog.js deleted file mode 100644 index 8c562d6b3e..0000000000 --- a/web/src/app/schedules/calendar-subscribe/CalendarSubscribeDeleteDialog.js +++ /dev/null @@ -1,37 +0,0 @@ -import React from 'react' -import { useMutation, gql } from '@apollo/client' -import { PropTypes as p } from 'prop-types' -import FormDialog from '../../dialogs/FormDialog' -import { nonFieldErrors } from '../../util/errutil' - -const mutation = gql` - mutation ($id: ID!) { - deleteAll(input: [{ id: $id, type: calendarSubscription }]) - } -` - -export default function CalendarSubscribeDeleteDialog(props) { - const [deleteSubscription, status] = useMutation(mutation, { - variables: { - id: props.calSubscriptionID, - }, - onCompleted: props.onClose, - }) - - return ( - - ) -} - -CalendarSubscribeDeleteDialog.propTypes = { - calSubscriptionID: p.string.isRequired, - onClose: p.func.isRequired, -} diff --git a/web/src/app/schedules/calendar-subscribe/CalendarSubscribeDeleteDialog.tsx b/web/src/app/schedules/calendar-subscribe/CalendarSubscribeDeleteDialog.tsx new file mode 100644 index 0000000000..d5de52312b --- /dev/null +++ b/web/src/app/schedules/calendar-subscribe/CalendarSubscribeDeleteDialog.tsx @@ -0,0 +1,42 @@ +import React, { ReactNode } from 'react' +import { useMutation, gql } from 'urql' +import FormDialog from '../../dialogs/FormDialog' +import { nonFieldErrors } from '../../util/errutil' + +const mutation = gql` + mutation ($id: ID!) { + deleteAll(input: [{ id: $id, type: calendarSubscription }]) + } +` + +interface CalendarSubscribeDeleteDialogProps { + calSubscriptionID: string + onClose: () => void +} + +export default function CalendarSubscribeDeleteDialog( + props: CalendarSubscribeDeleteDialogProps, +): ReactNode { + const [status, commit] = useMutation(mutation) + + return ( + + commit( + { + id: props.calSubscriptionID, + }, + { additionalTypenames: ['User'] }, + ).then((result) => { + if (!result.error) props.onClose() + }) + } + onClose={props.onClose} + /> + ) +} diff --git a/web/src/app/schedules/calendar-subscribe/CalendarSubscribeEditDialog.js b/web/src/app/schedules/calendar-subscribe/CalendarSubscribeEditDialog.tsx similarity index 56% rename from web/src/app/schedules/calendar-subscribe/CalendarSubscribeEditDialog.js rename to web/src/app/schedules/calendar-subscribe/CalendarSubscribeEditDialog.tsx index f635c58869..79feec96cd 100644 --- a/web/src/app/schedules/calendar-subscribe/CalendarSubscribeEditDialog.js +++ b/web/src/app/schedules/calendar-subscribe/CalendarSubscribeEditDialog.tsx @@ -1,12 +1,12 @@ -import React, { useState } from 'react' -import { useQuery, useMutation, gql } from '@apollo/client' -import { PropTypes as p } from 'prop-types' +import React, { ReactNode, useState } from 'react' +import { useQuery, useMutation, gql } from 'urql' import FormDialog from '../../dialogs/FormDialog' import CalendarSubscribeForm from './CalendarSubscribeForm' import { GenericError, ObjectNotFound } from '../../error-pages' import _ from 'lodash' import Spinner from '../../loading/components/Spinner' import { fieldErrors } from '../../util/errutil' +import { UserCalendarSubscription } from '../../../schema' const query = gql` query ($id: ID!) { @@ -25,7 +25,14 @@ const mutation = gql` } ` -export function CalendarSubscribeEditDialogContent(props) { +interface CalendarSubscribeEditDialogContentProps { + data: UserCalendarSubscription + onClose: () => void +} + +export function CalendarSubscribeEditDialogContent( + props: CalendarSubscribeEditDialogContentProps, +): ReactNode { const { data, onClose } = props // set default values from retrieved data @@ -36,27 +43,31 @@ export function CalendarSubscribeEditDialogContent(props) { }) // setup the mutation - const [updateSubscription, updateSubscriptionStatus] = useMutation(mutation, { - variables: { - input: { - id: props.data.id, - name: value.name, - fullSchedule: value.fullSchedule, - }, - }, - onCompleted: () => props.onClose(), - }) + const [updateSubscriptionStatus, commit] = useMutation(mutation) return ( updateSubscription()} + loading={updateSubscriptionStatus.fetching} + onSubmit={() => + commit( + { + input: { + id: props.data.id, + name: value.name, + fullSchedule: value.fullSchedule, + }, + }, + { additionalTypenames: ['UserCalendarSubscription'] }, + ).then((result) => { + if (!result.error) props.onClose() + }) + } form={ void } /* * Load edit data here before rendering edit content to * avoid breaking any rules of hooks */ -export default function CalendarSubscribeEditDialog(props) { - const { data, loading, error } = useQuery(query, { +export default function CalendarSubscribeEditDialog( + props: CalendarSubscribeEditDialogProps, +): ReactNode { + const [{ data, fetching, error }] = useQuery({ + query, variables: { id: props.calSubscriptionID }, }) if (error) return if (!_.get(data, 'userCalendarSubscription.id')) { - return loading ? : + return fetching ? : } return ( @@ -92,8 +106,3 @@ export default function CalendarSubscribeEditDialog(props) { /> ) } - -CalendarSubscribeEditDialog.propTypes = { - calSubscriptionID: p.string.isRequired, - onClose: p.func.isRequired, -} diff --git a/web/src/app/users/UserCalendarSubscriptionList.tsx b/web/src/app/users/UserCalendarSubscriptionList.tsx index 8df98ca88f..1e1b2ad5c9 100644 --- a/web/src/app/users/UserCalendarSubscriptionList.tsx +++ b/web/src/app/users/UserCalendarSubscriptionList.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react' +import React, { Suspense, useState } from 'react' import { useQuery, gql } from 'urql' import { Card, Alert } from '@mui/material' import FlatList, { FlatListListItem } from '../lists/FlatList' @@ -151,10 +151,12 @@ export default function UserCalendarSubscriptionList(props: { /> )} {showEditDialogByID && ( - setShowEditDialogByID(null)} - /> + + setShowEditDialogByID(null)} + /> + )} {showDeleteDialogByID && (