From 7c0d421bc2f397b128861eb56e5b54d858c9aa05 Mon Sep 17 00:00:00 2001 From: Hakan Shehu Date: Wed, 9 Jul 2025 18:01:08 +0200 Subject: [PATCH] Show the count and a list of records that have no value for calendar group field (#124) --- .../records/record-field-value-count.ts | 10 +-- .../records/record-field-value-count.ts | 2 +- .../board-view-columns-collaborator.tsx | 2 +- .../board-view-columns-multi-select.tsx | 2 +- .../boards/board-view-columns-select.tsx | 2 +- .../databases/calendars/calendar-view-day.tsx | 6 +- .../calendars/calendar-view-grid.tsx | 4 +- .../calendar-view-no-value-count.tsx | 76 +++++++++++++++++++ .../calendars/calendar-view-no-value-list.tsx | 63 +++++++++++++++ .../databases/calendars/calendar-view.tsx | 2 + 10 files changed, 157 insertions(+), 12 deletions(-) create mode 100644 packages/ui/src/components/databases/calendars/calendar-view-no-value-count.tsx create mode 100644 packages/ui/src/components/databases/calendars/calendar-view-no-value-list.tsx diff --git a/packages/client/src/handlers/queries/records/record-field-value-count.ts b/packages/client/src/handlers/queries/records/record-field-value-count.ts index df55e154..d7033128 100644 --- a/packages/client/src/handlers/queries/records/record-field-value-count.ts +++ b/packages/client/src/handlers/queries/records/record-field-value-count.ts @@ -35,7 +35,7 @@ export class RecordFieldValueCountQueryHandler ) { return { hasChanges: true, - result: { values: [], nullCount: 0 }, + result: { values: [], noValueCount: 0 }, }; } @@ -91,7 +91,7 @@ export class RecordFieldValueCountQueryHandler ) { return { hasChanges: true, - result: { values: [], nullCount: 0 }, + result: { values: [], noValueCount: 0 }, }; } @@ -121,7 +121,7 @@ export class RecordFieldValueCountQueryHandler if (!field) { return { values: [], - nullCount: 0, + noValueCount: 0, }; } @@ -141,12 +141,12 @@ export class RecordFieldValueCountQueryHandler const output: RecordFieldValueCountQueryOutput = { values: [], - nullCount: 0, + noValueCount: 0, }; for (const row of result.rows) { if (row.value === 'null') { - output.nullCount = row.count; + output.noValueCount = row.count; } else { output.values.push({ value: row.value, diff --git a/packages/client/src/queries/records/record-field-value-count.ts b/packages/client/src/queries/records/record-field-value-count.ts index 1d64a6e4..74b999be 100644 --- a/packages/client/src/queries/records/record-field-value-count.ts +++ b/packages/client/src/queries/records/record-field-value-count.ts @@ -16,7 +16,7 @@ export type RecordFieldValueCount = { export type RecordFieldValueCountQueryOutput = { values: RecordFieldValueCount[]; - nullCount: number; + noValueCount: number; }; declare module '@colanode/client/queries' { diff --git a/packages/ui/src/components/databases/boards/board-view-columns-collaborator.tsx b/packages/ui/src/components/databases/boards/board-view-columns-collaborator.tsx index 29d642c5..4b28a9d2 100644 --- a/packages/ui/src/components/databases/boards/board-view-columns-collaborator.tsx +++ b/packages/ui/src/components/databases/boards/board-view-columns-collaborator.tsx @@ -46,7 +46,7 @@ export const BoardViewColumnsCollaborator = ({ fieldId: field.id, operator: 'is_empty', }; - const noValueCount = collaboratorCountQuery.data?.nullCount ?? 0; + const noValueCount = collaboratorCountQuery.data?.noValueCount ?? 0; return ( <> diff --git a/packages/ui/src/components/databases/boards/board-view-columns-multi-select.tsx b/packages/ui/src/components/databases/boards/board-view-columns-multi-select.tsx index 7a1a8ba7..e2ddc576 100644 --- a/packages/ui/src/components/databases/boards/board-view-columns-multi-select.tsx +++ b/packages/ui/src/components/databases/boards/board-view-columns-multi-select.tsx @@ -45,7 +45,7 @@ export const BoardViewColumnsMultiSelect = ({ }; const selectOptionCount = selectOptionCountQuery.data?.values ?? []; - const noValueCount = selectOptionCountQuery.data?.nullCount ?? 0; + const noValueCount = selectOptionCountQuery.data?.noValueCount ?? 0; const noValueDraggingClass = getSelectOptionLightColorClass('gray'); diff --git a/packages/ui/src/components/databases/boards/board-view-columns-select.tsx b/packages/ui/src/components/databases/boards/board-view-columns-select.tsx index 282b5380..56c045c6 100644 --- a/packages/ui/src/components/databases/boards/board-view-columns-select.tsx +++ b/packages/ui/src/components/databases/boards/board-view-columns-select.tsx @@ -44,7 +44,7 @@ export const BoardViewColumnsSelect = ({ }; const selectOptionCount = selectOptionCountQuery.data?.values ?? []; - const noValueCount = selectOptionCountQuery.data?.nullCount ?? 0; + const noValueCount = selectOptionCountQuery.data?.noValueCount ?? 0; const noValueDraggingClass = getSelectOptionLightColorClass('gray'); diff --git a/packages/ui/src/components/databases/calendars/calendar-view-day.tsx b/packages/ui/src/components/databases/calendars/calendar-view-day.tsx index fd9218eb..4fb223af 100644 --- a/packages/ui/src/components/databases/calendars/calendar-view-day.tsx +++ b/packages/ui/src/components/databases/calendars/calendar-view-day.tsx @@ -42,7 +42,11 @@ export const CalendarViewDay = ({ /> )} -

+

{date.getDate()}

diff --git a/packages/ui/src/components/databases/calendars/calendar-view-grid.tsx b/packages/ui/src/components/databases/calendars/calendar-view-grid.tsx index b11a8b25..94542247 100644 --- a/packages/ui/src/components/databases/calendars/calendar-view-grid.tsx +++ b/packages/ui/src/components/databases/calendars/calendar-view-grid.tsx @@ -76,12 +76,12 @@ export const CalendarViewGrid = ({ field }: CalendarViewGridProps) => { ), button_previous: cn( buttonVariants({ variant: 'ghost' }), - 'size-(--cell-size) aria-disabled:opacity-50 p-0 select-none', + 'size-(--cell-size) aria-disabled:opacity-50 p-0 select-none size-7', defaultClassNames.button_previous ), button_next: cn( buttonVariants({ variant: 'ghost' }), - 'size-(--cell-size) aria-disabled:opacity-50 p-0 select-none', + 'size-(--cell-size) aria-disabled:opacity-50 p-0 select-none size-7', defaultClassNames.button_next ), month_caption: cn( diff --git a/packages/ui/src/components/databases/calendars/calendar-view-no-value-count.tsx b/packages/ui/src/components/databases/calendars/calendar-view-no-value-count.tsx new file mode 100644 index 00000000..19307668 --- /dev/null +++ b/packages/ui/src/components/databases/calendars/calendar-view-no-value-count.tsx @@ -0,0 +1,76 @@ +import { CircleDashed } from 'lucide-react'; + +import { DatabaseViewFilterAttributes, FieldAttributes } from '@colanode/core'; +import { CalendarViewNoValueList } from '@colanode/ui/components/databases/calendars/calendar-view-no-value-list'; +import { + Dialog, + DialogTrigger, + DialogContent, + DialogHeader, + DialogTitle, + DialogDescription, +} from '@colanode/ui/components/ui/dialog'; +import { useDatabase } from '@colanode/ui/contexts/database'; +import { useDatabaseView } from '@colanode/ui/contexts/database-view'; +import { useWorkspace } from '@colanode/ui/contexts/workspace'; +import { useQuery } from '@colanode/ui/hooks/use-query'; + +interface CalendarViewNoValueCountProps { + field: FieldAttributes; +} + +export const CalendarViewNoValueCount = ({ + field, +}: CalendarViewNoValueCountProps) => { + const workspace = useWorkspace(); + const database = useDatabase(); + const view = useDatabaseView(); + + const filters: DatabaseViewFilterAttributes[] = [ + ...view.filters, + { + id: 'no_value', + type: 'field', + fieldId: field.id, + operator: 'is_empty', + }, + ]; + + const noValueCountQuery = useQuery({ + type: 'record.field.value.count', + databaseId: database.id, + filters: filters, + fieldId: field.id, + accountId: workspace.accountId, + workspaceId: workspace.id, + }); + + const noValueCount = noValueCountQuery.data?.noValueCount ?? 0; + if (noValueCount === 0) { + return null; + } + + return ( + + +
+ +

No {field.name}

+

({noValueCount.toLocaleString()})

+
+
+ + + {view.name} + + Record with no {field.name} value ({noValueCount.toLocaleString()}) + + + + +
+ ); +}; diff --git a/packages/ui/src/components/databases/calendars/calendar-view-no-value-list.tsx b/packages/ui/src/components/databases/calendars/calendar-view-no-value-list.tsx new file mode 100644 index 00000000..fa2981ec --- /dev/null +++ b/packages/ui/src/components/databases/calendars/calendar-view-no-value-list.tsx @@ -0,0 +1,63 @@ +import { InView } from 'react-intersection-observer'; + +import { DatabaseViewFilterAttributes, FieldAttributes } from '@colanode/core'; +import { Avatar } from '@colanode/ui/components/avatars/avatar'; +import { useDatabaseView } from '@colanode/ui/contexts/database-view'; +import { useLayout } from '@colanode/ui/contexts/layout'; +import { useRecordsQuery } from '@colanode/ui/hooks/use-records-query'; + +interface CalendarViewNoValueListProps { + filters: DatabaseViewFilterAttributes[]; + field: FieldAttributes; +} + +export const CalendarViewNoValueList = ({ + filters, + field, +}: CalendarViewNoValueListProps) => { + const view = useDatabaseView(); + const layout = useLayout(); + + const { records, hasMore, loadMore, isPending } = useRecordsQuery( + filters, + view.sorts + ); + + return ( +
+ {records.length === 0 && ( +
+ No records with no {field.name} value +
+ )} + {records.map((record) => { + const name = record.attributes.name ?? 'Unnamed'; + return ( +
{ + layout.previewLeft(record.id, true); + }} + > + +

{name}

+
+ ); + })} + { + if (inView && hasMore && !isPending) { + loadMore(); + } + }} + > +
+ ); +}; diff --git a/packages/ui/src/components/databases/calendars/calendar-view.tsx b/packages/ui/src/components/databases/calendars/calendar-view.tsx index 11bc6741..3f497c1b 100644 --- a/packages/ui/src/components/databases/calendars/calendar-view.tsx +++ b/packages/ui/src/components/databases/calendars/calendar-view.tsx @@ -2,6 +2,7 @@ import { Fragment } from 'react'; import { CalendarViewGrid } from '@colanode/ui/components/databases/calendars/calendar-view-grid'; import { CalendarViewNoGroup } from '@colanode/ui/components/databases/calendars/calendar-view-no-group'; +import { CalendarViewNoValueCount } from '@colanode/ui/components/databases/calendars/calendar-view-no-value-count'; import { CalendarViewSettings } from '@colanode/ui/components/databases/calendars/calendar-view-settings'; import { ViewFilterButton } from '@colanode/ui/components/databases/search/view-filter-button'; import { ViewSearchBar } from '@colanode/ui/components/databases/search/view-search-bar'; @@ -23,6 +24,7 @@ export const CalendarView = () => {
+ {groupByField && }