Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
d77ffef
ui/dev: Refactor IntegrationKeyList and UniversalKeyDefaultActions to…
mastercactapus Mar 19, 2025
661cecb
Merge branch 'master' into complist-services
mastercactapus Mar 20, 2025
ea054c8
ui/dev: Refactor HeartbeatMonitorList to use CompList
mastercactapus Mar 20, 2025
a3df46b
ui/dev: Refactor UniversalKeyRuleList to use CompList and ReorderGrou…
mastercactapus Mar 20, 2025
3ac8944
ui/dev: Update CompListItemText to make title prop optional and enhan…
mastercactapus Mar 20, 2025
5aa6a8c
ui/dev: Refactor UserCalendarSubscriptionList to use CompList and Com…
mastercactapus Mar 20, 2025
4cfd3cf
ui/dev: Add highlight prop to CompListItemText for improved item sele…
mastercactapus Mar 20, 2025
bb192ae
Refactor UserSessionList to use CompList
mastercactapus Mar 20, 2025
41f37d6
ui/dev: Refactor UserOnCallAssignmentList to use CompList and CompLis…
mastercactapus Mar 20, 2025
3ff2363
ui/dev: Refactor UserList to use CompList and CompListItemNav for imp…
mastercactapus Mar 20, 2025
dbe30ef
ui/dev: Refactor UserContactMethodList to use CompList and CompListIt…
mastercactapus Mar 20, 2025
726b6fb
Refactor UserNotificationRuleList to use CompList and CompListItemTex…
mastercactapus Mar 20, 2025
232149d
Refactor schedule lists to use CompList and CompListItemNav/Text for …
mastercactapus Mar 21, 2025
65f726c
Refactor ScheduleShiftList to use CompList and CompListItemText for i…
mastercactapus Mar 21, 2025
e92505e
Merge branch 'master' into complist-users
mastercactapus Mar 26, 2025
c9abe58
Merge branch 'complist-users' into complist-sched
mastercactapus Mar 26, 2025
6e3d7c1
ci: add integration workflow for comprehensive testing
mastercactapus Apr 3, 2025
8b0d0ea
fix: add unique keys to CompListItemText components for error handling
mastercactapus Apr 4, 2025
6bba17b
Revert "ci: add integration workflow for comprehensive testing"
mastercactapus Apr 21, 2025
832564a
fix: add unique keys to CompListItemText components for improved rend…
mastercactapus Apr 23, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions web/src/app/lists/CompListItems.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -24,8 +28,10 @@ export function CompListItemText(
props: CompListItemTextProps,
): React.ReactNode {
return (
<ListItem>
{props.icon && <ListItemIcon tabIndex={-1}>{props.icon}</ListItemIcon>}
<ListItem className={props.highlight ? 'Mui-selected' : ''}>
{(props.icon || props.alwaysShowIcon) && (
<ListItemIcon tabIndex={-1}>{props.icon}</ListItemIcon>
)}
<ListItemText
disableTypography={props.disableTypography}
primary={props.title}
Expand All @@ -40,13 +46,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 (
<li>
<ListItemButton component={AppLink} to={props.url}>
<ListItemButton component={AppLink} to={props.url} newTab={props.newTab}>
{props.icon && <ListItemIcon tabIndex={-1}>{props.icon}</ListItemIcon>}
<ListItemText primary={props.title} secondary={props.subText} />
{props.action && (
Expand Down
21 changes: 11 additions & 10 deletions web/src/app/schedules/ScheduleAssignedToList.tsx
Original file line number Diff line number Diff line change
@@ -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!) {
Expand Down Expand Up @@ -36,15 +37,15 @@ export default function ScheduleAssignedToList(props: {

return (
<Card sx={{ width: '100%' }}>
<FlatList
items={data.schedule.assignedTo.map(
(t: { name: string; id: string }) => ({
title: t.name,
url: `/escalation-policies/${t.id}`,
}),
)}
emptyMessage='This schedule is not assigned to any escalation policies.'
/>
<CompList emptyMessage='This schedule is not assigned to any escalation policies.'>
{data.schedule.assignedTo.map((t: { name: string; id: string }) => (
<CompListItemNav
key={t.id}
title={t.name}
url={`/escalation-policies/${t.id}`}
/>
))}
</CompList>
</Card>
)
}
25 changes: 13 additions & 12 deletions web/src/app/schedules/ScheduleList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -67,17 +68,17 @@ export default function ScheduleList(): JSX.Element {
slots={{
search: <Search />,
list: (
<FlatList
emptyMessage='No results'
items={
q.data?.schedules.nodes.map((u) => ({
title: u.name,
subText: u.description,
url: u.id,
secondaryAction: u.isFavorite ? <FavoriteIcon /> : undefined,
})) || []
}
/>
<CompList emptyMessage='No results'>
{q.data?.schedules.nodes.map((u) => (
<CompListItemNav
key={u.id}
title={u.name}
subText={u.description}
url={u.id}
action={u.isFavorite ? <FavoriteIcon /> : undefined}
/>
))}
</CompList>
),
}}
/>
Expand Down
63 changes: 33 additions & 30 deletions web/src/app/schedules/ScheduleOverrideList.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -143,35 +144,10 @@ export default function ScheduleOverrideList({ scheduleID }) {
loading={q.fetching}
slots={{
list: (
<FlatList
<CompList
emptyMessage='No results'
headerNote={note}
items={
q.data?.userOverrides.nodes.map((n) => ({
title: n.addUser ? n.addUser.name : n.removeUser.name,
subText: subText(n),
icon: (
<UserAvatar
userID={n.addUser ? n.addUser.id : n.removeUser.id}
/>
),
secondaryAction: (
<OtherActions
actions={[
{
label: 'Edit',
onClick: () => setEditID(n.id),
},
{
label: 'Delete',
onClick: () => setDeleteID(n.id),
},
]}
/>
),
})) || []
}
headerAction={
note={note}
action={
<React.Fragment>
<FilterContainer onReset={() => resetFilter()}>
<Grid item xs={12}>
Expand Down Expand Up @@ -212,7 +188,34 @@ export default function ScheduleOverrideList({ scheduleID }) {
)}
</React.Fragment>
}
/>
>
{q.data?.userOverrides.nodes.map((n) => (
<CompListItemText
key={n.id}
title={n.addUser ? n.addUser.name : n.removeUser.name}
subText={subText(n)}
icon={
<UserAvatar
userID={n.addUser ? n.addUser.id : n.removeUser.id}
/>
}
action={
<OtherActions
actions={[
{
label: 'Edit',
onClick: () => setEditID(n.id),
},
{
label: 'Delete',
onClick: () => setDeleteID(n.id),
},
]}
/>
}
/>
))}
</CompList>
),
}}
/>
Expand Down
102 changes: 57 additions & 45 deletions web/src/app/schedules/ScheduleRuleList.tsx
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -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!) {
Expand Down Expand Up @@ -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(
<CompListItemText key={type} subText={startCase(type + 's')} />,
)
lastType = type
}

items.push({
title: name,
url: (type === 'rotation' ? '/rotations/' : '/users/') + id,
subText: renderSubText(tgt.rules, timeZone),
icon:
type === 'rotation' ? <RotationAvatar /> : <UserAvatar userID={id} />,
secondaryAction: (
<OtherActions
actions={[
{
label: 'Edit',
onClick: () => setEditTarget({ type, id }),
},
{
label: 'Delete',
onClick: () => setDeleteTarget({ type, id }),
},
]}
/>
),
})
items.push(
<CompListItemNav
key={id}
title={name}
newTab
url={(type === 'rotation' ? '/rotations/' : '/users/') + id}
subText={renderSubText(tgt.rules, timeZone)}
icon={
type === 'rotation' ? (
<RotationAvatar />
) : (
<UserAvatar userID={id} />
)
}
action={
<OtherActions
actions={[
{
label: 'Edit',
onClick: () => setEditTarget({ type, id }),
},
{
label: 'Delete',
onClick: () => setDeleteTarget({ type, id }),
},
]}
/>
}
/>,
)
})

return (
<React.Fragment>
<Card style={{ width: '100%', marginBottom: 64 }}>
<FlatList
headerNote={`Showing times in ${data.schedule.timeZone}.`}
items={items}
headerAction={
!isMobile ? (
<ButtonGroup variant='contained'>
<Button
startIcon={<GroupAdd />}
onClick={() => setCreateType('rotation')}
>
Add Rotation
</Button>
<Button
startIcon={<PersonAdd />}
onClick={() => setCreateType('user')}
>
Add User
</Button>
</ButtonGroup>
) : undefined
<CompList
note={`Showing times in ${data.schedule.timeZone}.`}
hideActionOnMobile
action={
<ButtonGroup variant='contained'>
<Button
startIcon={<GroupAdd />}
onClick={() => setCreateType('rotation')}
>
Add Rotation
</Button>
<Button
startIcon={<PersonAdd />}
onClick={() => setCreateType('user')}
>
Add User
</Button>
</ButtonGroup>
}
/>
>
{items}
</CompList>
</Card>

{isMobile && (
Expand Down
Loading
Loading