-
Notifications
You must be signed in to change notification settings - Fork 336
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Matt Krick <[email protected]>
- Loading branch information
Showing
10 changed files
with
311 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
import CheckBoxIcon from '@mui/icons-material/CheckBox' | ||
import IndeterminateCheckBoxIcon from '@mui/icons-material/IndeterminateCheckBox' | ||
import * as Checkbox from '@radix-ui/react-checkbox' | ||
import type {NodeViewProps} from '@tiptap/core' | ||
import graphql from 'babel-plugin-relay/macro' | ||
import dayjs from 'dayjs' | ||
import {useEffect} from 'react' | ||
import {usePreloadedQuery, type PreloadedQuery} from 'react-relay' | ||
import type {SpecificMeetingPickerQuery} from '../__generated__/SpecificMeetingPickerQuery.graphql' | ||
import type {InsightsBlockAttrs} from '../tiptap/extensions/imageBlock/InsightsBlock' | ||
import {MeetingTypeToReadable} from './MeetingTypePickerCombobox' | ||
const query = graphql` | ||
query SpecificMeetingPickerQuery( | ||
$after: DateTime! | ||
$first: Int! | ||
$before: DateTime! | ||
$meetingTypes: [MeetingTypeEnum!]! | ||
$teamIds: [ID!]! | ||
) { | ||
viewer { | ||
meetings( | ||
after: $after | ||
before: $before | ||
first: $first | ||
meetingTypes: $meetingTypes | ||
teamIds: $teamIds | ||
) { | ||
edges { | ||
node { | ||
id | ||
name | ||
createdAt | ||
meetingType | ||
team { | ||
name | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
` | ||
interface Props { | ||
updateAttributes: NodeViewProps['updateAttributes'] | ||
attrs: InsightsBlockAttrs | ||
queryRef: PreloadedQuery<SpecificMeetingPickerQuery> | ||
} | ||
|
||
export const SpecificMeetingPicker = (props: Props) => { | ||
const {updateAttributes, attrs, queryRef} = props | ||
const {meetingIds, after, before} = attrs | ||
const data = usePreloadedQuery<SpecificMeetingPickerQuery>(query, queryRef) | ||
const {viewer} = data | ||
const {meetings} = viewer | ||
const {edges} = meetings | ||
|
||
useEffect(() => { | ||
const meetingIds = edges.map(({node}) => node.id) | ||
updateAttributes({meetingIds}) | ||
}, [edges]) | ||
const rows = edges.map((edge) => { | ||
const {node} = edge | ||
const {id, createdAt, meetingType, name, team} = node | ||
const {name: teamName} = team | ||
return { | ||
id, | ||
createdAt, | ||
meetingType: MeetingTypeToReadable[meetingType], | ||
name, | ||
teamName | ||
// isSelected: meetingIds.includes(id) | ||
} | ||
}) | ||
const allColumns = ['Name', 'Date', 'Team', 'Type'] | ||
const ignoredTeamColumn = attrs.teamIds.length === 1 ? ['Team'] : [] | ||
const ignoredTypeColumn = attrs.meetingTypes.length === 1 ? ['Type'] : [] | ||
const ignoredColumns = ignoredTeamColumn.concat(ignoredTypeColumn) | ||
const columns = allColumns.filter((column) => !ignoredColumns.includes(column)) | ||
const includeYear = new Date(after).getFullYear() !== new Date(before).getFullYear() | ||
const formatter = includeYear ? 'MMM D, YYYY' : 'MMM D' | ||
const allChecked = meetingIds.length === edges.length | ||
const MultiIcon = allChecked ? CheckBoxIcon : IndeterminateCheckBoxIcon | ||
return ( | ||
<div className='flex max-h-52 overflow-auto'> | ||
<table className='w-full border-collapse'> | ||
<thead className='sticky top-0 z-10 bg-slate-200'> | ||
<tr className='border-b-[1px] border-slate-400'> | ||
<th className='w-5 border-b-[1px] border-b-transparent pt-1 pr-1'> | ||
<Checkbox.Root | ||
onClick={() => { | ||
const nextMeetingIds = allChecked ? [] : edges.map(({node}) => node.id) | ||
updateAttributes({meetingIds: nextMeetingIds}) | ||
}} | ||
checked={allChecked ? true : meetingIds.length === 0 ? false : 'indeterminate'} | ||
className={ | ||
'flex size-4 appearance-none items-center justify-center rounded-xs border-slate-600 bg-white outline-none data-[state=unchecked]:border-2' | ||
} | ||
> | ||
<Checkbox.Indicator asChild> | ||
<MultiIcon className='w-5 fill-sky-500' /> | ||
</Checkbox.Indicator> | ||
</Checkbox.Root> | ||
</th> | ||
{columns.map((column) => { | ||
return ( | ||
<th | ||
className={ | ||
'border-slate-400 p-2 text-left font-bold not-last-of-type:border-r-[1px]' | ||
} | ||
> | ||
{column} | ||
</th> | ||
) | ||
})} | ||
</tr> | ||
</thead> | ||
<tbody> | ||
{rows.map((row) => { | ||
const {createdAt, id, meetingType, name, teamName} = row | ||
const date = dayjs(createdAt).format(formatter) | ||
const checked = meetingIds.includes(id) | ||
return ( | ||
<tr | ||
className='cursor-pointer border-slate-400 not-last-of-type:border-b-[1px]' | ||
onClick={() => { | ||
const nextMeetingIds = checked | ||
? meetingIds.filter((id) => id !== row.id) | ||
: [...meetingIds, id] | ||
updateAttributes({meetingIds: nextMeetingIds}) | ||
}} | ||
> | ||
<td className='w-5 border-b-[1px] border-b-transparent pt-1'> | ||
<Checkbox.Root | ||
checked={checked} | ||
className={ | ||
'flex size-4 appearance-none items-center justify-center rounded-xs border-slate-600 bg-white outline-none data-[state=unchecked]:border-2' | ||
} | ||
> | ||
<Checkbox.Indicator asChild> | ||
<CheckBoxIcon className='w-5 fill-sky-500' /> | ||
</Checkbox.Indicator> | ||
</Checkbox.Root> | ||
</td> | ||
<td className='border-r-[1px] border-slate-400 p-2'>{name}</td> | ||
<td className='border-r-[1px] border-slate-400 p-2 last-of-type:border-r-0'> | ||
{date} | ||
</td> | ||
{columns.includes('Team') && ( | ||
<td className='border-r-[1px] border-slate-400 p-2 last-of-type:border-r-0'> | ||
{teamName} | ||
</td> | ||
)} | ||
{columns.includes('Type') && ( | ||
<td className='border-r-[1px] border-slate-400 p-2 last-of-type:border-r-0'> | ||
{meetingType} | ||
</td> | ||
)} | ||
</tr> | ||
) | ||
})} | ||
</tbody> | ||
</table> | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import type {NodeViewProps} from '@tiptap/core' | ||
import {Suspense} from 'react' | ||
import type {SpecificMeetingPickerQuery} from '../__generated__/SpecificMeetingPickerQuery.graphql' | ||
import query from '../__generated__/TeamPickerComboboxQuery.graphql' | ||
import useQueryLoaderNow from '../hooks/useQueryLoaderNow' | ||
import type {InsightsBlockAttrs} from '../tiptap/extensions/imageBlock/InsightsBlock' | ||
import {Loader} from '../utils/relay/renderLoader' | ||
import {SpecificMeetingPicker} from './SpecificMeetingPicker' | ||
|
||
interface Props { | ||
updateAttributes: NodeViewProps['updateAttributes'] | ||
attrs: InsightsBlockAttrs | ||
} | ||
|
||
export const SpecificMeetingPickerRoot = (props: Props) => { | ||
const {attrs, updateAttributes} = props | ||
const {teamIds, meetingTypes, after, before} = attrs | ||
const queryRef = useQueryLoaderNow<SpecificMeetingPickerQuery>(query, { | ||
teamIds, | ||
meetingTypes, | ||
after, | ||
before, | ||
first: 500 | ||
}) | ||
|
||
return ( | ||
<Suspense fallback={<Loader />}> | ||
{queryRef && ( | ||
<SpecificMeetingPicker | ||
queryRef={queryRef} | ||
attrs={attrs} | ||
updateAttributes={updateAttributes} | ||
/> | ||
)} | ||
</Suspense> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
14 changes: 14 additions & 0 deletions
14
packages/server/graphql/public/typeDefs/MeetingConnection.graphql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
""" | ||
A connection to list of meetings | ||
""" | ||
type MeetingConnection { | ||
""" | ||
Page info with cursors as strings | ||
""" | ||
pageInfo: PageInfoDateCursor! | ||
|
||
""" | ||
A list of edges. | ||
""" | ||
edges: [MeetingEdge!]! | ||
} |
10 changes: 10 additions & 0 deletions
10
packages/server/graphql/public/typeDefs/MeetingEdge.graphql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
""" | ||
An edge in a connection. | ||
""" | ||
type MeetingEdge { | ||
""" | ||
The item at the end of the edge | ||
""" | ||
node: NewMeeting! | ||
cursor: DateTime! | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.