diff --git a/web/src/app/lists/CompListItems.tsx b/web/src/app/lists/CompListItems.tsx index fb3ea30e46..3451d16c3a 100644 --- a/web/src/app/lists/CompListItems.tsx +++ b/web/src/app/lists/CompListItems.tsx @@ -12,8 +12,12 @@ import AppLink from '../util/AppLink' import { ExpandLess, ExpandMore } from '@mui/icons-material' export type CompListItemTextProps = { - title: React.ReactNode + title?: React.ReactNode icon?: React.ReactNode + /* Set to true to always create space for the icon, even if it is not set */ + alwaysShowIcon?: boolean + highlight?: boolean + subText?: React.ReactNode action?: React.ReactNode disableTypography?: boolean @@ -24,8 +28,10 @@ export function CompListItemText( props: CompListItemTextProps, ): React.ReactNode { return ( - - {props.icon && {props.icon}} + + {(props.icon || props.alwaysShowIcon) && ( + {props.icon} + )} - + {props.icon && {props.icon}} {props.action && ( diff --git a/web/src/app/schedules/ScheduleAssignedToList.tsx b/web/src/app/schedules/ScheduleAssignedToList.tsx index d2699c27c8..bf925d0365 100644 --- a/web/src/app/schedules/ScheduleAssignedToList.tsx +++ b/web/src/app/schedules/ScheduleAssignedToList.tsx @@ -1,9 +1,10 @@ import React from 'react' import { useQuery, gql } from 'urql' -import FlatList from '../lists/FlatList' import Card from '@mui/material/Card' import Spinner from '../loading/components/Spinner' import { GenericError } from '../error-pages' +import CompList from '../lists/CompList' +import { CompListItemNav } from '../lists/CompListItems' const query = gql` query ($id: ID!) { @@ -36,15 +37,15 @@ export default function ScheduleAssignedToList(props: { return ( - ({ - title: t.name, - url: `/escalation-policies/${t.id}`, - }), - )} - emptyMessage='This schedule is not assigned to any escalation policies.' - /> + + {data.schedule.assignedTo.map((t: { name: string; id: string }) => ( + + ))} + ) } diff --git a/web/src/app/schedules/ScheduleList.tsx b/web/src/app/schedules/ScheduleList.tsx index ec22f71283..d2046935cf 100644 --- a/web/src/app/schedules/ScheduleList.tsx +++ b/web/src/app/schedules/ScheduleList.tsx @@ -5,8 +5,9 @@ import { useURLParam } from '../actions' import { ScheduleConnection } from '../../schema' import ListPageControls from '../lists/ListPageControls' import Search from '../util/Search' -import FlatList from '../lists/FlatList' import { FavoriteIcon } from '../util/SetFavoriteButton' +import CompList from '../lists/CompList' +import { CompListItemNav } from '../lists/CompListItems' const query = gql` query schedulesQuery($input: ScheduleSearchOptions) { @@ -67,17 +68,17 @@ export default function ScheduleList(): JSX.Element { slots={{ search: , list: ( - ({ - title: u.name, - subText: u.description, - url: u.id, - secondaryAction: u.isFavorite ? : undefined, - })) || [] - } - /> + + {q.data?.schedules.nodes.map((u) => ( + : undefined} + /> + ))} + ), }} /> diff --git a/web/src/app/schedules/ScheduleOverrideList.jsx b/web/src/app/schedules/ScheduleOverrideList.jsx index 0c16bbd171..2446889654 100644 --- a/web/src/app/schedules/ScheduleOverrideList.jsx +++ b/web/src/app/schedules/ScheduleOverrideList.jsx @@ -19,7 +19,8 @@ import { defaultTempSchedValue } from './temp-sched/sharedUtils' import ScheduleOverrideDialog from './ScheduleOverrideDialog' import CreateFAB from '../lists/CreateFAB' import ListPageControls from '../lists/ListPageControls' -import FlatList from '../lists/FlatList' +import CompList from '../lists/CompList' +import { CompListItemText } from '../lists/CompListItems' const query = gql` query scheduleOverrides($input: UserOverrideSearchOptions) { @@ -143,35 +144,10 @@ export default function ScheduleOverrideList({ scheduleID }) { loading={q.fetching} slots={{ list: ( - ({ - title: n.addUser ? n.addUser.name : n.removeUser.name, - subText: subText(n), - icon: ( - - ), - secondaryAction: ( - setEditID(n.id), - }, - { - label: 'Delete', - onClick: () => setDeleteID(n.id), - }, - ]} - /> - ), - })) || [] - } - headerAction={ + note={note} + action={ resetFilter()}> @@ -212,7 +188,34 @@ export default function ScheduleOverrideList({ scheduleID }) { )} } - /> + > + {q.data?.userOverrides.nodes.map((n) => ( + + } + action={ + setEditID(n.id), + }, + { + label: 'Delete', + onClick: () => setDeleteID(n.id), + }, + ]} + /> + } + /> + ))} + ), }} /> diff --git a/web/src/app/schedules/ScheduleRuleList.tsx b/web/src/app/schedules/ScheduleRuleList.tsx index b38b856563..0dec9fbddf 100644 --- a/web/src/app/schedules/ScheduleRuleList.tsx +++ b/web/src/app/schedules/ScheduleRuleList.tsx @@ -1,6 +1,5 @@ import React, { Suspense, useState } from 'react' import { useQuery, gql } from 'urql' -import FlatList, { FlatListListItem } from '../lists/FlatList' import { Button, ButtonGroup, Card } from '@mui/material' import { GroupAdd, PersonAdd } from '@mui/icons-material' import Tooltip from '@mui/material/Tooltip' @@ -18,6 +17,8 @@ import { DateTime } from 'luxon' import { useScheduleTZ } from './useScheduleTZ' import { useIsWidthDown } from '../util/useWidth' import { ScheduleRule, ScheduleTarget, TargetType } from '../../schema' +import CompList from '../lists/CompList' +import { CompListItemNav, CompListItemText } from '../lists/CompListItems' const query = gql` query scheduleRules($id: ID!) { @@ -99,64 +100,75 @@ export default function ScheduleRuleList( targets: ScheduleTarget[], timeZone: string, ): JSX.Element { - const items: FlatListListItem[] = [] + const items: React.ReactNode[] = [] let lastType: TargetType sortBy(targets, ['target.type', 'target.name']).forEach((tgt) => { const { name, id, type } = tgt.target if (type !== lastType) { - items.push({ subHeader: startCase(type + 's') }) + items.push( + , + ) lastType = type } - items.push({ - title: name, - url: (type === 'rotation' ? '/rotations/' : '/users/') + id, - subText: renderSubText(tgt.rules, timeZone), - icon: - type === 'rotation' ? : , - secondaryAction: ( - setEditTarget({ type, id }), - }, - { - label: 'Delete', - onClick: () => setDeleteTarget({ type, id }), - }, - ]} - /> - ), - }) + items.push( + + ) : ( + + ) + } + action={ + setEditTarget({ type, id }), + }, + { + label: 'Delete', + onClick: () => setDeleteTarget({ type, id }), + }, + ]} + /> + } + />, + ) }) return ( - - - - - ) : undefined + + + + } - /> + > + {items} + {isMobile && ( diff --git a/web/src/app/schedules/ScheduleShiftList.tsx b/web/src/app/schedules/ScheduleShiftList.tsx index 4ea2f6f7d3..8396b8f759 100644 --- a/web/src/app/schedules/ScheduleShiftList.tsx +++ b/web/src/app/schedules/ScheduleShiftList.tsx @@ -14,7 +14,6 @@ import makeStyles from '@mui/styles/makeStyles' import { DateTime, DateTimeFormatOptions, Duration, Interval } from 'luxon' import React, { Suspense, useCallback, useMemo, useState } from 'react' import CreateFAB from '../lists/CreateFAB' -import FlatList, { FlatListListItem } from '../lists/FlatList' import { UserSelect } from '../selection' import { UserAvatar } from '../util/avatars' import FilterContainer from '../util/FilterContainer' @@ -31,6 +30,8 @@ import { defaultTempSchedValue, } from './temp-sched/sharedUtils' import TempSchedDialog from './temp-sched/TempSchedDialog' +import CompList from '../lists/CompList' +import { CompListItemText } from '../lists/CompListItems' // query name is important, as it's used for refetching data after mutations const query = gql` @@ -221,7 +222,7 @@ function ScheduleShiftList({ ) } - function items(): FlatListListItem[] { + function items(): React.ReactNode[] { const _shifts: Shift[] = data?.schedule?.shifts?.map((s: Shift) => ({ ...s, @@ -251,19 +252,25 @@ function ScheduleShiftList({ DateTime.fromISO(end).startOf('day'), ) - const result: FlatListListItem[] = [] + const result: React.ReactNode[] = [] displaySpan.splitBy({ days: 1 }).forEach((day) => { const dayShifts = shifts.filter((s) => day.overlaps(s.interval)) if (!dayShifts.length) return - result.push({ - subHeader: relativeDate(day.start), - }) + result.push( + , + ) dayShifts.forEach((s) => { - result.push({ - title: s.user?.name || '', - subText: getShiftDetails(s, day), - icon: , - }) + result.push( + } + />, + ) }) }) @@ -333,10 +340,9 @@ function ScheduleShiftList({ }} > - { @@ -396,7 +402,9 @@ function ScheduleShiftList({ )} } - /> + > + {items()} + {isMobile && ( - } - items={[]} /> @@ -102,28 +102,59 @@ export default function ScheduleOnCallNotificationsList({ - setCreateRule(true)} - startIcon={} - > - Create Notification Rule - - ) + hideActionOnMobile + action={ + } - items={q.data.schedule.onCallNotificationRules.map((rule) => { + > + {q.data.schedule.onCallNotificationRules.map((rule) => { const display = rule.dest.displayInfo if ('error' in display) { - return { - icon: , - title: `ERROR: ${display.error}`, - subText: 'Notifies ' + onCallRuleSummary(timeZone, rule), - secondaryAction: ( + return ( + } + title={`ERROR: ${display.error}`} + subText={`Notifies ${onCallRuleSummary(timeZone, rule)}`} + action={ + setEditRuleID(rule.id), + }, + { + label: 'Delete', + onClick: () => setDeleteRuleID(rule.id), + }, + ]} + /> + } + /> + ) + } + + return ( + + } + title={display.text} + subText={'Notifies ' + onCallRuleSummary(timeZone, rule)} + action={ - ), - } - } - - return { - icon: ( - - ), - title: display.text, - subText: 'Notifies ' + onCallRuleSummary(timeZone, rule), - secondaryAction: ( - setEditRuleID(rule.id), - }, - { - label: 'Delete', - onClick: () => setDeleteRuleID(rule.id), - }, - ]} - /> - ), - } + } + /> + ) })} - /> + diff --git a/web/src/app/users/UserCalendarSubscriptionList.tsx b/web/src/app/users/UserCalendarSubscriptionList.tsx index 1e1b2ad5c9..ca6ceaad1d 100644 --- a/web/src/app/users/UserCalendarSubscriptionList.tsx +++ b/web/src/app/users/UserCalendarSubscriptionList.tsx @@ -1,7 +1,6 @@ import React, { Suspense, useState } from 'react' import { useQuery, gql } from 'urql' import { Card, Alert } from '@mui/material' -import FlatList, { FlatListListItem } from '../lists/FlatList' import OtherActions from '../util/OtherActions' import CreateFAB from '../lists/CreateFAB' import CalendarSubscribeCreateDialog from '../schedules/calendar-subscribe/CalendarSubscribeCreateDialog' @@ -11,9 +10,10 @@ import CalendarSubscribeEditDialog from '../schedules/calendar-subscribe/Calenda import { GenericError, ObjectNotFound } from '../error-pages' import _ from 'lodash' import { useConfigValue } from '../util/RequireConfig' -import AppLink from '../util/AppLink' import { UserCalendarSubscription } from '../../schema' import { Time } from '../util/Time' +import CompList from '../lists/CompList' +import { CompListItemNav, CompListItemText } from '../lists/CompListItems' export const calendarSubscriptionsQuery = gql` query calendarSubscriptions($id: ID!) { @@ -71,7 +71,7 @@ export default function UserCalendarSubscriptionList(props: { }) const subheaderDict: { [key: string]: boolean } = {} - const items: FlatListListItem[] = [] + const items: React.ReactNode[] = [] function renderOtherActions(id: string): JSX.Element { return ( @@ -94,29 +94,32 @@ export default function UserCalendarSubscriptionList(props: { subs.forEach((sub: UserCalendarSubscription) => { if (!subheaderDict[sub?.schedule?.name ?? '']) { subheaderDict[sub?.schedule?.name ?? ''] = true - items.push({ - subHeader: ( - - {sub.schedule?.name} - - ), - }) + items.push( + , + ) } // push subscriptions under relevant schedule subheaders - items.push({ - title: sub.name, - subText: ( -