diff --git a/web/src/app/lists/CompListItems.tsx b/web/src/app/lists/CompListItems.tsx
index 5a5583f7da..072690d48c 100644
--- a/web/src/app/lists/CompListItems.tsx
+++ b/web/src/app/lists/CompListItems.tsx
@@ -36,6 +36,7 @@ export function CompListItemText(
disableTypography={props.disableTypography}
primary={props.title}
secondary={props.subText}
+ data-cy='comp-list-item-subheader'
/>
{props.action && (
{props.action}
@@ -46,13 +47,14 @@ export function CompListItemText(
export type CompListItemNavProps = CompListItemTextProps & {
url: string
+ newTab?: boolean
}
/* A list item that links to a URL. */
export function CompListItemNav(props: CompListItemNavProps): React.ReactNode {
return (
-
+
{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 (
-
- }
- onClick={() => setCreateType('rotation')}
- >
- Add Rotation
-
- }
- onClick={() => setCreateType('user')}
- >
- Add User
-
-
- ) : undefined
+
+ }
+ onClick={() => setCreateType('rotation')}
+ >
+ Add Rotation
+
+ }
+ onClick={() => setCreateType('user')}
+ >
+ Add User
+
+
}
- />
+ >
+ {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/cypress/e2e/schedules.cy.ts b/web/src/cypress/e2e/schedules.cy.ts
index 93ba9f9ce3..c99ff6c321 100644
--- a/web/src/cypress/e2e/schedules.cy.ts
+++ b/web/src/cypress/e2e/schedules.cy.ts
@@ -155,8 +155,8 @@ function testSchedules(screen: ScreenFormat): void {
}).then(() => {
cy.engineTrigger()
cy.get('[data-cy="route-links"] li').contains('Shifts').click()
- cy.get('[data-cy=flat-list-item-subheader]').should('contain', 'Today')
- cy.get('[data-cy=flat-list-item-subheader]').should(
+ cy.get('[data-cy=comp-list-item-subheader]').should('contain', 'Today')
+ cy.get('[data-cy=comp-list-item-subheader]').should(
'contain',
'Tomorrow',
)