@agent-native/core-corpus 0.210.2 → 0.211.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +1 -1
- package/corpus/templates/analytics/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +41 -2
- package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +4 -2
- package/corpus/templates/analytics/CHANGELOG.md +18 -0
- package/corpus/templates/analytics/actions/build-data-index.ts +11 -2
- package/corpus/templates/analytics/actions/configure-content-collection.ts +29 -4
- package/corpus/templates/analytics/actions/configure-first-party-events-model.ts +9 -7
- package/corpus/templates/analytics/actions/create-analytics-public-key.ts +4 -0
- package/corpus/templates/analytics/actions/estimate-content-signups-cost.ts +72 -0
- package/corpus/templates/analytics/actions/estimate-query-cost.ts +16 -134
- package/corpus/templates/analytics/actions/list-sigma-workbooks.ts +123 -42
- package/corpus/templates/analytics/actions/migrate-sigma-workbook.ts +20 -17
- package/corpus/templates/analytics/actions/revoke-analytics-public-key.ts +4 -0
- package/corpus/templates/analytics/app/components/dashboard/SigmaMigrationDialog.tsx +246 -680
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +1 -1
- package/corpus/templates/analytics/app/i18n/en-US.ts +54 -55
- package/corpus/templates/analytics/app/pages/DashboardOverview.tsx +0 -1
- package/corpus/templates/analytics/app/pages/DataSources.tsx +56 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/filter-vars.ts +4 -3
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +26 -8
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/interpolate.ts +3 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/panel-cost-notice.ts +172 -0
- package/corpus/templates/analytics/app/pages/data-sources/SearchConsoleDetail.tsx +441 -0
- package/corpus/templates/analytics/changelog/2026-10-15-a-content-dashboard-lists-every-blog-article-with-its-author.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-15-data-sources-has-a-google-search-console-card-that-shows-the.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-16-the-content-dashboard-can-show-signups-per-blog-article.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-19-creating-or-revoking-a-public-analytics-key-now-asks-you-to-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-19-search-sigma-workbooks-by-name-and-open-ones-a-teammate-alre.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-blog-signup-attribution-excludes-signups-before-the-first-vi.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-collapsed-analytics-navigation-starts-at-the-top.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-content-keeps-successful-coverage-dates-and-missing-day-gaps.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-private-github-repositories-can-be-indexed-with-your-connect.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-search-console-preserves-backfill-and-paused-collection.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-search-console-refreshes-historical-coverage-when-properties.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-search-console-setup-keeps-saved-properties-readable-and-sup.md +6 -0
- package/corpus/templates/analytics/seeds/dashboards/content-view.json +182 -0
- package/corpus/templates/analytics/server/db/schema.ts +1 -0
- package/corpus/templates/analytics/server/lib/bi-migration/external-dashboards-store.ts +56 -18
- package/corpus/templates/analytics/server/lib/bi-migration/unfinished-import.ts +64 -0
- package/corpus/templates/analytics/server/lib/builtin-dashboard-pass.ts +17 -2
- package/corpus/templates/analytics/server/lib/content-metrics/collect.ts +2 -0
- package/corpus/templates/analytics/server/lib/content-metrics/config.ts +92 -3
- package/corpus/templates/analytics/server/lib/content-metrics/legacy-content-view.json +172 -0
- package/corpus/templates/analytics/server/lib/content-metrics/migration.ts +2 -0
- package/corpus/templates/analytics/server/lib/content-metrics/steps/daily-metrics.ts +8 -3
- package/corpus/templates/analytics/server/lib/content-metrics/steps/search-console.ts +16 -3
- package/corpus/templates/analytics/server/lib/content-metrics/steps/signups.ts +240 -0
- package/corpus/templates/analytics/server/lib/content-metrics/store.ts +5 -0
- package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +21 -0
- package/corpus/templates/analytics/server/lib/dashboard-panel-verification.ts +300 -5
- package/corpus/templates/analytics/server/lib/dashboard-seeds.ts +2 -0
- package/corpus/templates/analytics/server/lib/dashboards-store.ts +28 -0
- package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +128 -24
- package/corpus/templates/analytics/server/lib/google-analytics.ts +56 -16
- package/corpus/templates/analytics/server/lib/index-build-github.ts +5 -0
- package/corpus/templates/analytics/server/lib/provider-credentials.ts +47 -2
- package/corpus/templates/analytics/server/lib/query-cost-estimate.ts +184 -5
- package/corpus/templates/analytics/server/plugins/db.ts +9 -1
- package/corpus/templates/analytics/shared/builtin-dashboard-ids.ts +2 -0
- package/corpus/templates/analytics/shared/search-console-property.ts +78 -0
- package/corpus/templates/assets/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/brain/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/calendar/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/calendar/actions/delete-event.ts +1 -3
- package/corpus/templates/calendar/actions/delete-events.ts +1 -2
- package/corpus/templates/calendar/actions/event-action-helpers.ts +0 -4
- package/corpus/templates/calendar/actions/update-event.ts +2 -6
- package/corpus/templates/chat/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/clips/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/clips/CHANGELOG.md +11 -24
- package/corpus/templates/clips/actions/create-recording-upload.ts +5 -1
- package/corpus/templates/clips/app/components/player/chapter-list.tsx +7 -6
- package/corpus/templates/clips/app/components/player/comment-composer.tsx +5 -1
- package/corpus/templates/clips/app/components/player/player-controls.tsx +1 -0
- package/corpus/templates/clips/app/components/player/scrubber.tsx +4 -2
- package/corpus/templates/clips/app/i18n/en-US.ts +3 -3
- package/corpus/templates/clips/app/lib/extension-auth.ts +70 -0
- package/corpus/templates/clips/app/root.tsx +22 -10
- package/corpus/templates/clips/app/routes/_app.notifications.tsx +3 -0
- package/corpus/templates/clips/app/routes/import.tsx +115 -110
- package/corpus/templates/clips/changelog/2026-10-16-agent-uploads-can-use-free-clips-storage.md +6 -0
- package/corpus/templates/clips/changelog/2026-10-19-agents-see-a-clip-s-frames-as-soon-as-it-finishes-processing.md +6 -0
- package/corpus/templates/clips/changelog/2026-10-19-chapter-list-times-now-match-the-player-s-clock-and-each-cha.md +6 -0
- package/corpus/templates/clips/changelog/2026-10-19-editing-a-comment-puts-the-cursor-after-its-text-and-reply-o.md +6 -0
- package/corpus/templates/clips/changelog/2026-10-19-youtube-import-followups.md +6 -0
- package/corpus/templates/clips/changelog/2026-10-20-locally-saved-desktop-recordings-now-appear-in-the-recovery-.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +29 -186
- package/corpus/templates/clips/desktop/src/components/RecordingRecovery.tsx +171 -16
- package/corpus/templates/clips/desktop/src/lib/record-first.ts +9 -32
- package/corpus/templates/clips/desktop/src/lib/recording-recovery.ts +30 -1
- package/corpus/templates/clips/server/lib/agent-frame-request.ts +5 -2
- package/corpus/templates/clips/server/lib/recording-upload-ticket.ts +8 -0
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +2 -0
- package/corpus/templates/content/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/content/CHANGELOG.md +12 -0
- package/corpus/templates/content/actions/_content-database-personal-view.ts +5 -15
- package/corpus/templates/content/actions/_database-navigation.ts +3 -3
- package/corpus/templates/content/actions/_database-utils.ts +10 -22
- package/corpus/templates/content/actions/_database-view-setup.ts +16 -76
- package/corpus/templates/content/actions/create-comment-ai-suggestion.ts +93 -44
- package/corpus/templates/content/actions/export-document.ts +2 -14
- package/corpus/templates/content/actions/suggest-document-edit.ts +5 -7
- package/corpus/templates/content/actions/update-content-database-view.ts +2 -14
- package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +86 -30
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +64 -28
- package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +6 -46
- package/corpus/templates/content/app/components/editor/PageDraftRecovery.tsx +72 -7
- package/corpus/templates/content/app/components/editor/TableHoverControls.tsx +292 -103
- package/corpus/templates/content/app/components/editor/ThreadSuggestion.tsx +147 -40
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +108 -11
- package/corpus/templates/content/app/components/editor/database/ContentTable.tsx +2 -1
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +150 -166
- package/corpus/templates/content/app/components/editor/database/database-cell-comments.tsx +515 -0
- package/corpus/templates/content/app/components/editor/database/database-item-comments.tsx +5 -43
- package/corpus/templates/content/app/components/editor/database/filter-sort.ts +8 -783
- package/corpus/templates/content/app/components/editor/live-editor-session.ts +5 -1
- package/corpus/templates/content/app/components/editor/page-draft-journal.ts +61 -1
- package/corpus/templates/content/app/components/editor/property-comments.tsx +35 -4
- package/corpus/templates/content/app/components/editor/property-date-format.ts +45 -0
- package/corpus/templates/content/app/components/editor/thread-suggestions.ts +30 -0
- package/corpus/templates/content/app/components/editor/useDocumentReconcileRecovery.ts +23 -4
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +36 -10
- package/corpus/templates/content/app/global.css +23 -0
- package/corpus/templates/content/app/i18n-data.ts +1 -0
- package/corpus/templates/content/changelog/2026-10-16-opening-a-page-in-a-new-tab-while-another-tab-is-still-savin.md +6 -0
- package/corpus/templates/content/changelog/2026-10-16-typing-steadily-in-one-tab-no-longer-makes-another-open-tab-.md +6 -0
- package/corpus/templates/content/changelog/2026-10-19-database-views-can-filter-the-same-property-more-than-once-n.md +6 -0
- package/corpus/templates/content/changelog/2026-10-19-edits-made-from-a-block-s-menu-or-a-table-s-row-and-column-m.md +6 -0
- package/corpus/templates/content/changelog/2026-10-19-starting-a-comment-puts-the-cursor-in-the-comment-box-ready-.md +6 -0
- package/corpus/templates/content/changelog/2026-10-19-table-row-and-column-options-now-appear-for-the-row-or-colum.md +6 -0
- package/corpus/templates/content/docs/product/capabilities/content.comment.page-owned.md +5 -1
- package/corpus/templates/content/docs/product/capabilities/content.revision.suggestions.md +3 -1
- package/corpus/templates/content/e2e/helpers.ts +10 -0
- package/corpus/templates/content/shared/api.ts +18 -12
- package/corpus/templates/content/shared/database-query.ts +107 -13
- package/corpus/templates/content/shared/suggestion-diff.ts +35 -0
- package/corpus/templates/crm/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/design/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/design/CHANGELOG.md +2 -0
- package/corpus/templates/design/actions/delete-file.ts +1 -4
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +10 -1
- package/corpus/templates/design/app/components/visual-editor/ReviewCanvasPins.tsx +24 -4
- package/corpus/templates/design/app/pages/design-editor/commands/mode-change.ts +14 -9
- package/corpus/templates/design/app/pages/design-editor/domains/use-editor-modes.ts +23 -2
- package/corpus/templates/design/app/pages/design-editor/tool-state.ts +29 -0
- package/corpus/templates/design/changelog/2026-10-19-reply-and-edit-on-a-comment-in-the-sidebar-or-on-a-canvas-pi.md +6 -0
- package/corpus/templates/design/changelog/2026-10-20-comments-return-to-the-mode-they-came-from-and-layers-search.md +6 -0
- package/corpus/templates/design/e2e/global-setup.ts +185 -85
- package/corpus/templates/dispatch/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/dispatch/CHANGELOG.md +4 -0
- package/corpus/templates/dispatch/actions/apply-workspace-connection-setup.ts +4 -0
- package/corpus/templates/dispatch/actions/delete-workspace-connection.ts +4 -0
- package/corpus/templates/dispatch/actions/set-workspace-connection-grant.ts +4 -0
- package/corpus/templates/dispatch/actions/upsert-workspace-connection.ts +4 -0
- package/corpus/templates/dispatch/changelog/2026-10-19-vault-secrets-grants-link-tokens-app-access-and-workspace-co.md +6 -0
- package/corpus/templates/factory/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +105 -33
- package/corpus/templates/factory/.agents/skills/review-latest-feedback/references/channel-inventory.md +128 -74
- package/corpus/templates/factory/.agents/skills/review-latest-feedback/references/slack-reaction-replies.md +39 -11
- package/corpus/templates/forms/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/mail/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/mail/.agents/skills/contacts-and-crm/SKILL.md +22 -17
- package/corpus/templates/mail/AGENTS.md +1 -1
- package/corpus/templates/mail/CHANGELOG.md +14 -0
- package/corpus/templates/mail/actions/provider-api-request.ts +3 -3
- package/corpus/templates/mail/app/components/email/IntegrationsSidebar.tsx +211 -54
- package/corpus/templates/mail/app/hooks/use-inbox-threads.ts +26 -2
- package/corpus/templates/mail/app/i18n/en-US.ts +5 -1
- package/corpus/templates/mail/app/lib/gong-connection.ts +27 -0
- package/corpus/templates/mail/app/lib/thread-cache.ts +110 -9
- package/corpus/templates/mail/app/root.tsx +10 -2
- package/corpus/templates/mail/changelog/2026-10-20-connected-gong-settings-now-open-the-shared-integration-mana.md +6 -0
- package/corpus/templates/mail/changelog/2026-10-20-gong-connection-details-now-match-the-call-data-available-in.md +6 -0
- package/corpus/templates/mail/changelog/2026-10-20-improve-gong-setup-with-a-click-to-connect-flow-and-permissi.md +6 -0
- package/corpus/templates/mail/changelog/2026-10-20-mail-lets-you-retry-when-it-cannot-check-your-gong-access.md +6 -0
- package/corpus/templates/mail/changelog/2026-10-20-mail-thread-cache-stays-isolated-when-mailbox-scope-changes.md +6 -0
- package/corpus/templates/mail/changelog/2026-10-20-the-inbox-and-recently-opened-threads-now-open-instantly-fro.md +6 -0
- package/corpus/templates/mail/server/handlers/emails.ts +9 -0
- package/corpus/templates/plan/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/plan/CHANGELOG.md +4 -0
- package/corpus/templates/plan/app/pages/PlansPage.tsx +173 -33
- package/corpus/templates/plan/changelog/2026-10-19-opening-a-comment-thread-puts-the-cursor-in-the-reply-box-an.md +6 -0
- package/corpus/templates/slides/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +6 -42
- package/corpus/templates/slides/app/components/editor/FloatingToolbar.tsx +3 -7
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +5 -14
- package/corpus/templates/slides/app/i18n/en-US.ts +0 -2
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +27 -33
- package/corpus/templates/slides/scripts/edit-fidelity/run.ts +1 -3
- package/corpus/templates/tasks/.agents/skills/actions/references/action-fields.md +5 -3
- package/package.json +1 -1
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
import { useSession } from "@agent-native/core/client/hooks";
|
|
2
|
+
import { useT } from "@agent-native/core/client/i18n";
|
|
3
|
+
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
|
4
|
+
import type { ContentDatabaseItem, DocumentProperty } from "@shared/api";
|
|
5
|
+
import { commentPropertyTarget } from "@shared/comment-property-target";
|
|
6
|
+
import {
|
|
7
|
+
IconLayoutSidebarRight,
|
|
8
|
+
IconMessage,
|
|
9
|
+
IconMessagePlus,
|
|
10
|
+
} from "@tabler/icons-react";
|
|
11
|
+
import { useQueryClient } from "@tanstack/react-query";
|
|
12
|
+
import {
|
|
13
|
+
type MutableRefObject,
|
|
14
|
+
useContext,
|
|
15
|
+
useEffect,
|
|
16
|
+
useMemo,
|
|
17
|
+
useRef,
|
|
18
|
+
useState,
|
|
19
|
+
} from "react";
|
|
20
|
+
import { toast } from "sonner";
|
|
21
|
+
|
|
22
|
+
import {
|
|
23
|
+
Tooltip,
|
|
24
|
+
TooltipContent,
|
|
25
|
+
TooltipTrigger,
|
|
26
|
+
} from "@/components/ui/tooltip";
|
|
27
|
+
import {
|
|
28
|
+
type CommentThread,
|
|
29
|
+
collectionRowsShowingDocumentFilter,
|
|
30
|
+
useComments,
|
|
31
|
+
} from "@/hooks/use-comments";
|
|
32
|
+
import { cn } from "@/lib/utils";
|
|
33
|
+
|
|
34
|
+
import {
|
|
35
|
+
CommentDraftProvider,
|
|
36
|
+
useCommentPanelSession,
|
|
37
|
+
} from "../comment-drafts";
|
|
38
|
+
import { CommentIconButton } from "../CommentRow";
|
|
39
|
+
import {
|
|
40
|
+
CommentsSidebar,
|
|
41
|
+
useCommentReplyDrafts,
|
|
42
|
+
usePendingCommentDraft,
|
|
43
|
+
} from "../CommentsSidebar";
|
|
44
|
+
import { deviceCannotHover } from "./ContentTable";
|
|
45
|
+
import {
|
|
46
|
+
DatabaseItemCommentsContext,
|
|
47
|
+
databaseCommentMarkerClassName,
|
|
48
|
+
} from "./database-item-comments";
|
|
49
|
+
|
|
50
|
+
type CellCommentCard = {
|
|
51
|
+
/** `start` opens on an empty composer, `read` on the cell's open threads. */
|
|
52
|
+
mode: "start" | "read";
|
|
53
|
+
key: number;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const NO_THREADS: CommentThread[] = [];
|
|
57
|
+
|
|
58
|
+
// Cards holding unsent text. While one does, no other cell opens a card,
|
|
59
|
+
// which would replace the row's saved draft with an empty one.
|
|
60
|
+
const cardsHoldingText = new Set<MutableRefObject<boolean>>();
|
|
61
|
+
|
|
62
|
+
/** Who can start, and who can resolve, Comments on a row's Page. */
|
|
63
|
+
export function databaseItemCommentAccess(item: ContentDatabaseItem) {
|
|
64
|
+
const { document } = item;
|
|
65
|
+
if (document.source?.mode === "local-files") {
|
|
66
|
+
return { canComment: false, canResolve: false };
|
|
67
|
+
}
|
|
68
|
+
const role = document.accessRole;
|
|
69
|
+
return {
|
|
70
|
+
canComment:
|
|
71
|
+
document.canComment ??
|
|
72
|
+
(role === "owner" ||
|
|
73
|
+
role === "admin" ||
|
|
74
|
+
role === "editor" ||
|
|
75
|
+
role === "commenter"),
|
|
76
|
+
canResolve: document.canEdit === true,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function isEditableTarget(target: EventTarget | null) {
|
|
81
|
+
return (
|
|
82
|
+
target instanceof Element &&
|
|
83
|
+
!!target.closest("[contenteditable='true'], input, textarea")
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* A table cell's Comments: a marker when its Property has open threads, and a
|
|
89
|
+
* button on hover or focus that starts one. Both open a card on the cell, so
|
|
90
|
+
* reading, replying, and starting a thread happen without leaving the table.
|
|
91
|
+
* Without a provider, cells show no Comment controls.
|
|
92
|
+
*/
|
|
93
|
+
export function DatabaseCellComments({
|
|
94
|
+
item,
|
|
95
|
+
property,
|
|
96
|
+
rowSelected = false,
|
|
97
|
+
besideEditButton = false,
|
|
98
|
+
}: {
|
|
99
|
+
item: ContentDatabaseItem;
|
|
100
|
+
property: DocumentProperty;
|
|
101
|
+
/**
|
|
102
|
+
* Touch screens can't hover, so there the Comment button shows on a
|
|
103
|
+
* selected row's cells, like the row's selection controls.
|
|
104
|
+
*/
|
|
105
|
+
rowSelected?: boolean;
|
|
106
|
+
/** The cell's value editor keeps its own button at the cell's end. */
|
|
107
|
+
besideEditButton?: boolean;
|
|
108
|
+
}) {
|
|
109
|
+
const t = useT();
|
|
110
|
+
const { session } = useSession();
|
|
111
|
+
const openComments = useContext(DatabaseItemCommentsContext);
|
|
112
|
+
const [card, setCard] = useState<CellCommentCard | null>(null);
|
|
113
|
+
const cardKeyRef = useRef(0);
|
|
114
|
+
const openerRef = useRef<HTMLButtonElement | null>(null);
|
|
115
|
+
const controlsRef = useRef<HTMLSpanElement>(null);
|
|
116
|
+
const startRef = useRef<HTMLButtonElement>(null);
|
|
117
|
+
// Set while the card holds unsent text, so a stray click or Escape keeps it.
|
|
118
|
+
const keepOpenRef = useRef(false);
|
|
119
|
+
// Set when focus belongs elsewhere once the card closes: wherever the user
|
|
120
|
+
// clicked, or the row's peek, which closes if focus returns to the table.
|
|
121
|
+
const skipFocusReturnRef = useRef(false);
|
|
122
|
+
const [layer, setLayer] = useState<HTMLDivElement | null>(null);
|
|
123
|
+
const [overflowing, setOverflowing] = useState(false);
|
|
124
|
+
useEffect(() => {
|
|
125
|
+
if (!layer || typeof ResizeObserver === "undefined") return;
|
|
126
|
+
const measure = () =>
|
|
127
|
+
setOverflowing(layer.scrollHeight > layer.clientHeight + 1);
|
|
128
|
+
const observer = new ResizeObserver(measure);
|
|
129
|
+
observer.observe(layer);
|
|
130
|
+
for (const child of layer.children) observer.observe(child);
|
|
131
|
+
measure();
|
|
132
|
+
return () => observer.disconnect();
|
|
133
|
+
}, [layer, card?.key]);
|
|
134
|
+
|
|
135
|
+
if (!openComments) return null;
|
|
136
|
+
const { canComment, canResolve } = databaseItemCommentAccess(item);
|
|
137
|
+
const propertyId = property.definition.id;
|
|
138
|
+
const name = property.definition.name;
|
|
139
|
+
const summary = item.comments?.properties[propertyId];
|
|
140
|
+
if (!summary && !canComment && !card) return null;
|
|
141
|
+
|
|
142
|
+
const open = (mode: CellCommentCard["mode"], opener: HTMLButtonElement) => {
|
|
143
|
+
// A card holding unsent text stays as it is, and no other card opens.
|
|
144
|
+
if (cardsHoldingText.size > 0) return;
|
|
145
|
+
if (card?.mode === mode) {
|
|
146
|
+
close();
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
// Switching an open card to a new comment remounts only the card, so
|
|
150
|
+
// the composer takes focus once the clicked button lets go of it.
|
|
151
|
+
if (card && mode === "start") opener.blur();
|
|
152
|
+
openerRef.current = opener;
|
|
153
|
+
skipFocusReturnRef.current = false;
|
|
154
|
+
setCard({ mode, key: ++cardKeyRef.current });
|
|
155
|
+
};
|
|
156
|
+
const close = () => setCard(null);
|
|
157
|
+
// Once its last thread is resolved, the marker that opened the card goes
|
|
158
|
+
// away, so focus returns to the cell's Comment button instead.
|
|
159
|
+
const settle = () => {
|
|
160
|
+
const start = startRef.current;
|
|
161
|
+
// A touch screen hides it outside a selected row.
|
|
162
|
+
if (start && (start.checkVisibility?.() ?? true)) {
|
|
163
|
+
openerRef.current = start;
|
|
164
|
+
}
|
|
165
|
+
close();
|
|
166
|
+
};
|
|
167
|
+
const startLabel = t("comments.commentOnProperty", { name });
|
|
168
|
+
const markerLabel = summary
|
|
169
|
+
? t("comments.openPropertyComments", { count: summary.open, name })
|
|
170
|
+
: "";
|
|
171
|
+
|
|
172
|
+
return (
|
|
173
|
+
<PopoverPrimitive.Root
|
|
174
|
+
open={!!card}
|
|
175
|
+
onOpenChange={(next) => {
|
|
176
|
+
if (!next) close();
|
|
177
|
+
}}
|
|
178
|
+
>
|
|
179
|
+
<PopoverPrimitive.Anchor asChild>
|
|
180
|
+
<span
|
|
181
|
+
className="pointer-events-none absolute inset-0"
|
|
182
|
+
aria-hidden="true"
|
|
183
|
+
/>
|
|
184
|
+
</PopoverPrimitive.Anchor>
|
|
185
|
+
<span
|
|
186
|
+
ref={controlsRef}
|
|
187
|
+
className="relative ms-auto flex min-h-6 shrink-0 items-center"
|
|
188
|
+
>
|
|
189
|
+
{canComment ? (
|
|
190
|
+
<Tooltip>
|
|
191
|
+
<TooltipTrigger asChild>
|
|
192
|
+
<button
|
|
193
|
+
ref={startRef}
|
|
194
|
+
type="button"
|
|
195
|
+
className={cn(
|
|
196
|
+
"pointer-events-none absolute end-full top-0 flex size-6 items-center justify-center rounded bg-background text-muted-foreground opacity-0 transition-opacity hover:bg-accent hover:text-foreground focus-visible:pointer-events-auto focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring group-focus-within/cell:pointer-events-auto group-focus-within/cell:opacity-100 group-hover/cell:pointer-events-auto group-hover/cell:opacity-100 [@media(any-hover:none)]:hidden",
|
|
197
|
+
besideEditButton ? "me-6" : "me-0.5",
|
|
198
|
+
card?.mode === "start" && "pointer-events-auto opacity-100",
|
|
199
|
+
rowSelected &&
|
|
200
|
+
"[@media(any-hover:none)]:pointer-events-auto [@media(any-hover:none)]:flex [@media(any-hover:none)]:opacity-100",
|
|
201
|
+
)}
|
|
202
|
+
aria-label={startLabel}
|
|
203
|
+
aria-haspopup="dialog"
|
|
204
|
+
aria-expanded={card?.mode === "start"}
|
|
205
|
+
data-database-cell-comment-start
|
|
206
|
+
onClick={(event) => {
|
|
207
|
+
event.stopPropagation();
|
|
208
|
+
// A card is cramped under a phone's keyboard, so touch
|
|
209
|
+
// screens write the comment in the row's peek.
|
|
210
|
+
if (deviceCannotHover()) {
|
|
211
|
+
if (keepOpenRef.current) return;
|
|
212
|
+
// The peek closes if focus returns to the table.
|
|
213
|
+
skipFocusReturnRef.current = true;
|
|
214
|
+
close();
|
|
215
|
+
openComments(item, null, propertyId);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
open("start", event.currentTarget);
|
|
219
|
+
}}
|
|
220
|
+
>
|
|
221
|
+
<IconMessagePlus className="size-3.5" aria-hidden="true" />
|
|
222
|
+
</button>
|
|
223
|
+
</TooltipTrigger>
|
|
224
|
+
<TooltipContent>{startLabel}</TooltipContent>
|
|
225
|
+
</Tooltip>
|
|
226
|
+
) : null}
|
|
227
|
+
{summary ? (
|
|
228
|
+
<Tooltip>
|
|
229
|
+
<TooltipTrigger asChild>
|
|
230
|
+
<button
|
|
231
|
+
type="button"
|
|
232
|
+
className={databaseCommentMarkerClassName}
|
|
233
|
+
aria-label={markerLabel}
|
|
234
|
+
aria-haspopup="dialog"
|
|
235
|
+
aria-expanded={card?.mode === "read"}
|
|
236
|
+
data-database-cell-comments={summary.open}
|
|
237
|
+
onClick={(event) => {
|
|
238
|
+
event.stopPropagation();
|
|
239
|
+
open("read", event.currentTarget);
|
|
240
|
+
}}
|
|
241
|
+
>
|
|
242
|
+
<IconMessage className="size-3.5" aria-hidden="true" />
|
|
243
|
+
{summary.open > 1 ? summary.open : null}
|
|
244
|
+
</button>
|
|
245
|
+
</TooltipTrigger>
|
|
246
|
+
<TooltipContent>{markerLabel}</TooltipContent>
|
|
247
|
+
</Tooltip>
|
|
248
|
+
) : null}
|
|
249
|
+
</span>
|
|
250
|
+
<PopoverPrimitive.Portal>
|
|
251
|
+
<PopoverPrimitive.Content
|
|
252
|
+
ref={setLayer}
|
|
253
|
+
align="end"
|
|
254
|
+
sideOffset={4}
|
|
255
|
+
collisionPadding={12}
|
|
256
|
+
className="database-cell-comment-card"
|
|
257
|
+
data-database-cell-comment-card={card?.mode}
|
|
258
|
+
data-overflowing={overflowing ? "" : undefined}
|
|
259
|
+
onOpenAutoFocus={(event) => {
|
|
260
|
+
if (card?.mode !== "start") return;
|
|
261
|
+
// The composer takes focus once it mounts, but only from the page.
|
|
262
|
+
event.preventDefault();
|
|
263
|
+
if (globalThis.document.activeElement instanceof HTMLElement) {
|
|
264
|
+
globalThis.document.activeElement.blur();
|
|
265
|
+
}
|
|
266
|
+
}}
|
|
267
|
+
onCloseAutoFocus={(event) => {
|
|
268
|
+
event.preventDefault();
|
|
269
|
+
const opener = openerRef.current;
|
|
270
|
+
if (!skipFocusReturnRef.current && opener?.isConnected) {
|
|
271
|
+
opener.focus({ preventScroll: true });
|
|
272
|
+
}
|
|
273
|
+
}}
|
|
274
|
+
onEscapeKeyDown={(event) => {
|
|
275
|
+
// A composer's Escape closes its menus or cancels an empty draft.
|
|
276
|
+
if (keepOpenRef.current || isEditableTarget(event.target)) {
|
|
277
|
+
event.preventDefault();
|
|
278
|
+
}
|
|
279
|
+
}}
|
|
280
|
+
onInteractOutside={(event) => {
|
|
281
|
+
// The cell's own buttons switch or close the card when clicked.
|
|
282
|
+
if (
|
|
283
|
+
keepOpenRef.current ||
|
|
284
|
+
(event.target instanceof Node &&
|
|
285
|
+
controlsRef.current?.contains(event.target))
|
|
286
|
+
) {
|
|
287
|
+
event.preventDefault();
|
|
288
|
+
} else {
|
|
289
|
+
skipFocusReturnRef.current = true;
|
|
290
|
+
}
|
|
291
|
+
}}
|
|
292
|
+
>
|
|
293
|
+
{card ? (
|
|
294
|
+
<CommentDraftProvider
|
|
295
|
+
documentId={item.document.id}
|
|
296
|
+
currentUserEmail={session?.email}
|
|
297
|
+
currentUserOrgId={session?.orgId}
|
|
298
|
+
>
|
|
299
|
+
<DatabaseCellCommentCard
|
|
300
|
+
key={card.key}
|
|
301
|
+
item={item}
|
|
302
|
+
property={property}
|
|
303
|
+
mode={card.mode}
|
|
304
|
+
canComment={canComment}
|
|
305
|
+
canResolve={canResolve}
|
|
306
|
+
currentUserEmail={session?.email ?? undefined}
|
|
307
|
+
currentUserOrgId={session?.orgId ?? undefined}
|
|
308
|
+
keepOpenRef={keepOpenRef}
|
|
309
|
+
onClose={close}
|
|
310
|
+
onSettled={settle}
|
|
311
|
+
onOpenRow={(threadId) => {
|
|
312
|
+
// The peek returns focus to the cell when it closes.
|
|
313
|
+
skipFocusReturnRef.current = true;
|
|
314
|
+
openerRef.current?.focus({ preventScroll: true });
|
|
315
|
+
close();
|
|
316
|
+
openComments(item, threadId);
|
|
317
|
+
}}
|
|
318
|
+
/>
|
|
319
|
+
</CommentDraftProvider>
|
|
320
|
+
) : null}
|
|
321
|
+
</PopoverPrimitive.Content>
|
|
322
|
+
</PopoverPrimitive.Portal>
|
|
323
|
+
</PopoverPrimitive.Root>
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function DatabaseCellCommentCard({
|
|
328
|
+
item,
|
|
329
|
+
property,
|
|
330
|
+
mode,
|
|
331
|
+
canComment,
|
|
332
|
+
canResolve,
|
|
333
|
+
currentUserEmail,
|
|
334
|
+
currentUserOrgId,
|
|
335
|
+
keepOpenRef,
|
|
336
|
+
onClose,
|
|
337
|
+
onSettled,
|
|
338
|
+
onOpenRow,
|
|
339
|
+
}: {
|
|
340
|
+
item: ContentDatabaseItem;
|
|
341
|
+
property: DocumentProperty;
|
|
342
|
+
mode: CellCommentCard["mode"];
|
|
343
|
+
canComment: boolean;
|
|
344
|
+
canResolve: boolean;
|
|
345
|
+
currentUserEmail?: string;
|
|
346
|
+
currentUserOrgId?: string;
|
|
347
|
+
keepOpenRef: MutableRefObject<boolean>;
|
|
348
|
+
onClose: () => void;
|
|
349
|
+
onSettled: () => void;
|
|
350
|
+
onOpenRow: (threadId: string) => void;
|
|
351
|
+
}) {
|
|
352
|
+
const t = useT();
|
|
353
|
+
const queryClient = useQueryClient();
|
|
354
|
+
const documentId = item.document.id;
|
|
355
|
+
const propertyId = property.definition.id;
|
|
356
|
+
const { data: threads, isLoading, refetch } = useComments(documentId);
|
|
357
|
+
// The marker can be newer than this row's cached threads, so a card opened
|
|
358
|
+
// from it reads the threads again before deciding the cell has none.
|
|
359
|
+
const [listedSinceOpen, setListedSinceOpen] = useState(mode === "start");
|
|
360
|
+
const replyDrafts = useCommentReplyDrafts(documentId, currentUserEmail);
|
|
361
|
+
const {
|
|
362
|
+
pendingComment,
|
|
363
|
+
setPendingComment,
|
|
364
|
+
changePendingComment,
|
|
365
|
+
completePendingComment,
|
|
366
|
+
} = usePendingCommentDraft(documentId);
|
|
367
|
+
// After posting, the card shows the new thread rather than every thread.
|
|
368
|
+
const [postedThreadId, setPostedThreadId] = useState<string | null>(null);
|
|
369
|
+
|
|
370
|
+
useEffect(() => {
|
|
371
|
+
if (mode === "read") {
|
|
372
|
+
const marked = item.comments?.properties[propertyId]?.open ?? 0;
|
|
373
|
+
void refetch()
|
|
374
|
+
.then(({ data, error }) => {
|
|
375
|
+
// With nothing cached either, the card closes; say why.
|
|
376
|
+
if (!data && error) {
|
|
377
|
+
toast.error(t("empty.genericError"), {
|
|
378
|
+
description: error instanceof Error ? error.message : undefined,
|
|
379
|
+
});
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
const open = data?.filter(
|
|
383
|
+
(thread) =>
|
|
384
|
+
thread.property?.propertyId === propertyId && !thread.resolved,
|
|
385
|
+
).length;
|
|
386
|
+
// A marker that no longer matches refreshes the row's counts.
|
|
387
|
+
if (open !== undefined && open !== marked) {
|
|
388
|
+
void queryClient.invalidateQueries({
|
|
389
|
+
...collectionRowsShowingDocumentFilter(documentId),
|
|
390
|
+
refetchType: "active",
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
})
|
|
394
|
+
.finally(() => setListedSinceOpen(true));
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
setPendingComment({
|
|
398
|
+
quotedText: "",
|
|
399
|
+
offsetTop: 0,
|
|
400
|
+
property: commentPropertyTarget(property),
|
|
401
|
+
databaseId: item.databaseId,
|
|
402
|
+
});
|
|
403
|
+
// Runs once per card; the card remounts each time it opens.
|
|
404
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
405
|
+
}, []);
|
|
406
|
+
|
|
407
|
+
const propertyThreads = useMemo(
|
|
408
|
+
() =>
|
|
409
|
+
threads?.filter((thread) => thread.property?.propertyId === propertyId) ??
|
|
410
|
+
NO_THREADS,
|
|
411
|
+
[threads, propertyId],
|
|
412
|
+
);
|
|
413
|
+
const openThreads = propertyThreads.filter(
|
|
414
|
+
(thread) =>
|
|
415
|
+
!thread.resolved &&
|
|
416
|
+
(!postedThreadId || thread.threadId === postedThreadId),
|
|
417
|
+
);
|
|
418
|
+
const postedThread = postedThreadId
|
|
419
|
+
? propertyThreads.find((thread) => thread.threadId === postedThreadId)
|
|
420
|
+
: undefined;
|
|
421
|
+
// A thread being resolved stays on screen until the server confirms it,
|
|
422
|
+
// so a failed resolve leaves it in the card with its error.
|
|
423
|
+
const { isResolving } = useCommentPanelSession();
|
|
424
|
+
const resolvingIds = propertyThreads
|
|
425
|
+
.filter((thread) => isResolving(thread.threadId))
|
|
426
|
+
.map((thread) => thread.threadId)
|
|
427
|
+
.join(" ");
|
|
428
|
+
const shownThreads = useMemo(() => {
|
|
429
|
+
if (!resolvingIds) return propertyThreads;
|
|
430
|
+
const resolving = new Set(resolvingIds.split(" "));
|
|
431
|
+
return propertyThreads.map((thread) =>
|
|
432
|
+
thread.resolved && resolving.has(thread.threadId)
|
|
433
|
+
? { ...thread, resolved: false }
|
|
434
|
+
: thread,
|
|
435
|
+
);
|
|
436
|
+
}, [propertyThreads, resolvingIds]);
|
|
437
|
+
// Resolving the last thread, or the one just posted, closes the card, as
|
|
438
|
+
// does a read that found none or failed.
|
|
439
|
+
const settled =
|
|
440
|
+
!pendingComment &&
|
|
441
|
+
!resolvingIds &&
|
|
442
|
+
(postedThreadId
|
|
443
|
+
? postedThread?.resolved === true
|
|
444
|
+
: mode === "read" && listedSinceOpen && openThreads.length === 0);
|
|
445
|
+
useEffect(() => {
|
|
446
|
+
if (settled) onSettled();
|
|
447
|
+
}, [settled, onSettled]);
|
|
448
|
+
|
|
449
|
+
const openReplyThreadId = replyDrafts.openReply?.threadId ?? null;
|
|
450
|
+
// Text still being written or sent; a failed send puts it back.
|
|
451
|
+
const unsent =
|
|
452
|
+
!!pendingComment?.submitting ||
|
|
453
|
+
!!pendingComment?.text.trim() ||
|
|
454
|
+
(!!openReplyThreadId && !!replyDrafts.get(openReplyThreadId).text.trim()) ||
|
|
455
|
+
propertyThreads.some((thread) => replyDrafts.isSubmitting(thread.threadId));
|
|
456
|
+
useEffect(() => {
|
|
457
|
+
keepOpenRef.current = unsent;
|
|
458
|
+
if (unsent) cardsHoldingText.add(keepOpenRef);
|
|
459
|
+
return () => {
|
|
460
|
+
keepOpenRef.current = false;
|
|
461
|
+
cardsHoldingText.delete(keepOpenRef);
|
|
462
|
+
};
|
|
463
|
+
}, [keepOpenRef, unsent]);
|
|
464
|
+
|
|
465
|
+
return (
|
|
466
|
+
<CommentsSidebar
|
|
467
|
+
compact
|
|
468
|
+
surface="popover"
|
|
469
|
+
presentation="inline"
|
|
470
|
+
alignToAnchors={false}
|
|
471
|
+
forceVisible
|
|
472
|
+
replyDrafts={replyDrafts}
|
|
473
|
+
documentId={documentId}
|
|
474
|
+
// While composing, the card shows only the new comment.
|
|
475
|
+
threads={pendingComment ? NO_THREADS : shownThreads}
|
|
476
|
+
visibleThreadId={postedThreadId}
|
|
477
|
+
documentProperties={item.properties}
|
|
478
|
+
isLoading={
|
|
479
|
+
!pendingComment &&
|
|
480
|
+
(isLoading || (!listedSinceOpen && openThreads.length === 0))
|
|
481
|
+
}
|
|
482
|
+
pendingComment={pendingComment}
|
|
483
|
+
onPendingChange={changePendingComment}
|
|
484
|
+
onPendingDone={(id, threadId) => {
|
|
485
|
+
if (!completePendingComment(id)) return;
|
|
486
|
+
if (threadId) setPostedThreadId(threadId);
|
|
487
|
+
else onClose();
|
|
488
|
+
}}
|
|
489
|
+
currentUserEmail={currentUserEmail}
|
|
490
|
+
currentUserOrgId={currentUserOrgId}
|
|
491
|
+
canComment={canComment}
|
|
492
|
+
canResolve={canResolve}
|
|
493
|
+
onClose={onClose}
|
|
494
|
+
renderThreadActions={(thread) =>
|
|
495
|
+
thread.threadId.startsWith("optimistic-") ? null : (
|
|
496
|
+
<Tooltip>
|
|
497
|
+
<TooltipTrigger asChild>
|
|
498
|
+
<CommentIconButton
|
|
499
|
+
aria-label={t("comments.openRow")}
|
|
500
|
+
data-database-cell-comment-open-row
|
|
501
|
+
onClick={(event) => {
|
|
502
|
+
event.stopPropagation();
|
|
503
|
+
onOpenRow(thread.threadId);
|
|
504
|
+
}}
|
|
505
|
+
>
|
|
506
|
+
<IconLayoutSidebarRight size={18} />
|
|
507
|
+
</CommentIconButton>
|
|
508
|
+
</TooltipTrigger>
|
|
509
|
+
<TooltipContent>{t("comments.openRow")}</TooltipContent>
|
|
510
|
+
</Tooltip>
|
|
511
|
+
)
|
|
512
|
+
}
|
|
513
|
+
/>
|
|
514
|
+
);
|
|
515
|
+
}
|
|
@@ -11,17 +11,19 @@ import {
|
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Opens a row with its Comments showing: one thread when `threadId` is set,
|
|
14
|
-
*
|
|
14
|
+
* a new comment on the Property `propertyId` names, otherwise the list.
|
|
15
|
+
* Without a provider, rows show no Comment markers.
|
|
15
16
|
*/
|
|
16
17
|
export type OpenDatabaseItemComments = (
|
|
17
18
|
item: ContentDatabaseItem,
|
|
18
19
|
threadId: string | null,
|
|
20
|
+
propertyId?: string,
|
|
19
21
|
) => void;
|
|
20
22
|
|
|
21
23
|
export const DatabaseItemCommentsContext =
|
|
22
24
|
createContext<OpenDatabaseItemComments | null>(null);
|
|
23
25
|
|
|
24
|
-
const
|
|
26
|
+
export const databaseCommentMarkerClassName =
|
|
25
27
|
"flex h-6 min-w-6 shrink-0 items-center justify-center gap-0.5 rounded px-1 text-xs font-medium focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring comment-marker";
|
|
26
28
|
|
|
27
29
|
/** The count of a row's open Comment threads, beside its name. */
|
|
@@ -43,7 +45,7 @@ export function DatabaseRowCommentCount({
|
|
|
43
45
|
<TooltipTrigger asChild>
|
|
44
46
|
<button
|
|
45
47
|
type="button"
|
|
46
|
-
className={
|
|
48
|
+
className={databaseCommentMarkerClassName}
|
|
47
49
|
aria-label={label}
|
|
48
50
|
data-database-row-comments={count}
|
|
49
51
|
onClick={(event) => {
|
|
@@ -59,43 +61,3 @@ export function DatabaseRowCommentCount({
|
|
|
59
61
|
</Tooltip>
|
|
60
62
|
);
|
|
61
63
|
}
|
|
62
|
-
|
|
63
|
-
/** Marks a cell whose Property has open Comment threads. */
|
|
64
|
-
export function DatabaseCellCommentMarker({
|
|
65
|
-
item,
|
|
66
|
-
propertyId,
|
|
67
|
-
propertyName,
|
|
68
|
-
}: {
|
|
69
|
-
item: ContentDatabaseItem;
|
|
70
|
-
propertyId: string;
|
|
71
|
-
propertyName: string;
|
|
72
|
-
}) {
|
|
73
|
-
const t = useT();
|
|
74
|
-
const openComments = useContext(DatabaseItemCommentsContext);
|
|
75
|
-
const summary = item.comments?.properties[propertyId];
|
|
76
|
-
if (!openComments || !summary) return null;
|
|
77
|
-
const label = t("comments.openPropertyComments", {
|
|
78
|
-
count: summary.open,
|
|
79
|
-
name: propertyName,
|
|
80
|
-
});
|
|
81
|
-
return (
|
|
82
|
-
<Tooltip>
|
|
83
|
-
<TooltipTrigger asChild>
|
|
84
|
-
<button
|
|
85
|
-
type="button"
|
|
86
|
-
className={markerClassName}
|
|
87
|
-
aria-label={label}
|
|
88
|
-
data-database-cell-comments={summary.open}
|
|
89
|
-
onClick={(event) => {
|
|
90
|
-
event.stopPropagation();
|
|
91
|
-
openComments(item, summary.latestThreadId);
|
|
92
|
-
}}
|
|
93
|
-
>
|
|
94
|
-
<IconMessage className="size-3.5" aria-hidden="true" />
|
|
95
|
-
{summary.open > 1 ? summary.open : null}
|
|
96
|
-
</button>
|
|
97
|
-
</TooltipTrigger>
|
|
98
|
-
<TooltipContent>{label}</TooltipContent>
|
|
99
|
-
</Tooltip>
|
|
100
|
-
);
|
|
101
|
-
}
|