Skip to content

Commit

Permalink
ui: convert CalendarSubscribe dialogs to ts and to use urql instead o…
Browse files Browse the repository at this point in the history
…f apollo (#3590)

* convert to ts and use urql instead of apollo

* user more granular cache update

* add typename
  • Loading branch information
Forfold committed Jan 25, 2024
1 parent 13acd08 commit b73a323
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 108 deletions.
Original file line number Diff line number Diff line change
@@ -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!) {
Expand All @@ -18,63 +17,55 @@ 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.'

return isComplete ? completedSubtitle : defaultSubtitle
}

export function getForm(isComplete, defaultForm, data) {
export function getForm(
isComplete: boolean,
defaultForm: ReactNode,
data: { createUserCalendarSubscription: UserCalendarSubscription },
): ReactNode {
return isComplete ? (
<CalenderSuccessForm url={data.createUserCalendarSubscription.url} />
) : (
defaultForm
)
}

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,
reminderMinutes: [],
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 = (
<CalendarSubscribeForm
errors={fieldErrors(status.error)}
loading={status.loading}
loading={status.fetching}
onChange={setValue}
scheduleReadOnly={Boolean(props.scheduleID)}
value={value}
Expand All @@ -85,8 +76,13 @@ export default function CalendarSubscribeCreateDialog(props) {
<FormDialog
title={
isComplete ? (
<div className={classes.successTitle}>
<SuccessIcon className={classes.successIcon} />
<div
style={{
display: 'flex',
alignItems: 'center',
}}
>
<SuccessIcon sx={{ marginRight: (theme) => theme.spacing(1) }} />
<Typography>Success!</Typography>
</div>
) : (
Expand All @@ -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,
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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 (
<FormDialog
title='Are you sure?'
confirm
loading={status.fetching}
errors={nonFieldErrors(status.error)}
subTitle='This will delete the calendar subscription.'
onSubmit={() =>
commit(
{
id: props.calSubscriptionID,
},
{ additionalTypenames: ['User'] },
).then((result) => {
if (!result.error) props.onClose()
})
}
onClose={props.onClose}
/>
)
}
Original file line number Diff line number Diff line change
@@ -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!) {
Expand All @@ -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
Expand All @@ -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 (
<FormDialog
title='Edit Calendar Subscription'
onClose={onClose}
loading={updateSubscriptionStatus.loading}
onSubmit={() => 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={
<CalendarSubscribeForm
errors={fieldErrors(updateSubscriptionStatus.error)}
loading={updateSubscriptionStatus.loading}
loading={updateSubscriptionStatus.fetching}
onChange={setValue}
value={value}
scheduleReadOnly
Expand All @@ -66,23 +77,26 @@ export function CalendarSubscribeEditDialogContent(props) {
)
}

CalendarSubscribeEditDialogContent.propTypes = {
data: p.object.isRequired,
onClose: p.func.isRequired,
interface CalendarSubscribeEditDialogProps {
calSubscriptionID: string
onClose: () => 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 <GenericError error={error.message} />
if (!_.get(data, 'userCalendarSubscription.id')) {
return loading ? <Spinner /> : <ObjectNotFound />
return fetching ? <Spinner /> : <ObjectNotFound />
}

return (
Expand All @@ -92,8 +106,3 @@ export default function CalendarSubscribeEditDialog(props) {
/>
)
}

CalendarSubscribeEditDialog.propTypes = {
calSubscriptionID: p.string.isRequired,
onClose: p.func.isRequired,
}
12 changes: 7 additions & 5 deletions web/src/app/users/UserCalendarSubscriptionList.tsx
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -151,10 +151,12 @@ export default function UserCalendarSubscriptionList(props: {
/>
)}
{showEditDialogByID && (
<CalendarSubscribeEditDialog
calSubscriptionID={showEditDialogByID}
onClose={() => setShowEditDialogByID(null)}
/>
<Suspense>
<CalendarSubscribeEditDialog
calSubscriptionID={showEditDialogByID}
onClose={() => setShowEditDialogByID(null)}
/>
</Suspense>
)}
{showDeleteDialogByID && (
<CalendarSubscribeDeleteDialog
Expand Down

0 comments on commit b73a323

Please sign in to comment.