Fix DocumentList: clear selection on filter change, update connection ID, fix indentation
Addresses PR review comments: clears selected document IDs when changing the type filter to prevent bulk actions on hidden rows, updates the Relay connection ID when the filter changes so mutations target the correct connection, and fixes eslint indentation violations. Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
@@ -6,7 +6,7 @@ import {
|
|||||||
IconPlusLarge,
|
IconPlusLarge,
|
||||||
PageHeader,
|
PageHeader,
|
||||||
} from "@probo/ui";
|
} from "@probo/ui";
|
||||||
import { useState } from "react";
|
import { useMemo, useState } from "react";
|
||||||
import {
|
import {
|
||||||
type PreloadedQuery,
|
type PreloadedQuery,
|
||||||
usePreloadedQuery,
|
usePreloadedQuery,
|
||||||
@@ -67,17 +67,21 @@ export default function DocumentsPage(props: {
|
|||||||
({ node: { canSendSigningNotifications } }) => canSendSigningNotifications,
|
({ node: { canSendSigningNotifications } }) => canSendSigningNotifications,
|
||||||
);
|
);
|
||||||
|
|
||||||
const [documentListConnectionId, setDocumentListConnectionId] = useState(
|
const unfilteredConnectionId = useMemo(
|
||||||
ConnectionHandler.getConnectionID(
|
() =>
|
||||||
organizationId,
|
ConnectionHandler.getConnectionID(
|
||||||
"DocumentsListQuery_documents",
|
organizationId,
|
||||||
{
|
"DocumentsListQuery_documents",
|
||||||
orderBy: { direction: "ASC", field: "TITLE" },
|
{
|
||||||
filter: { documentTypes: null },
|
orderBy: { direction: "ASC", field: "TITLE" },
|
||||||
},
|
filter: { documentTypes: null },
|
||||||
),
|
},
|
||||||
|
),
|
||||||
|
[organizationId],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const [, setDocumentListConnectionId] = useState(unfilteredConnectionId);
|
||||||
|
|
||||||
const handleSendSigningNotifications = async () => {
|
const handleSendSigningNotifications = async () => {
|
||||||
await sendSigningNotifications({
|
await sendSigningNotifications({
|
||||||
variables: {
|
variables: {
|
||||||
@@ -104,7 +108,7 @@ export default function DocumentsPage(props: {
|
|||||||
)}
|
)}
|
||||||
{organization.canCreateDocument && (
|
{organization.canCreateDocument && (
|
||||||
<CreateDocumentDialog
|
<CreateDocumentDialog
|
||||||
connection={documentListConnectionId}
|
connection={unfilteredConnectionId}
|
||||||
trigger={
|
trigger={
|
||||||
<Button icon={IconPlusLarge}>{__("New document")}</Button>
|
<Button icon={IconPlusLarge}>{__("New document")}</Button>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,7 +86,18 @@ export function DocumentList(props: {
|
|||||||
|
|
||||||
const handleDocumentTypeFilterChange = (value: string) => {
|
const handleDocumentTypeFilterChange = (value: string) => {
|
||||||
const newType = value === "ALL" ? null : (value as DocumentType);
|
const newType = value === "ALL" ? null : (value as DocumentType);
|
||||||
|
clear();
|
||||||
setDocumentTypeFilter(newType);
|
setDocumentTypeFilter(newType);
|
||||||
|
onConnectionIdChange(
|
||||||
|
ConnectionHandler.getConnectionID(
|
||||||
|
organizationId,
|
||||||
|
"DocumentsListQuery_documents",
|
||||||
|
{
|
||||||
|
orderBy: { direction: "ASC", field: "TITLE" },
|
||||||
|
filter: { documentTypes: newType ? [newType] : null },
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
startTransition(() => {
|
startTransition(() => {
|
||||||
pagination.refetch(
|
pagination.refetch(
|
||||||
{ documentTypes: newType ? [newType] : null },
|
{ documentTypes: newType ? [newType] : null },
|
||||||
@@ -165,7 +176,7 @@ export function DocumentList(props: {
|
|||||||
onValueChange={handleDocumentTypeFilterChange}
|
onValueChange={handleDocumentTypeFilterChange}
|
||||||
>
|
>
|
||||||
<Option value="ALL">{__("All types")}</Option>
|
<Option value="ALL">{__("All types")}</Option>
|
||||||
{documentTypes.map((type) => (
|
{documentTypes.map(type => (
|
||||||
<Option key={type} value={type}>
|
<Option key={type} value={type}>
|
||||||
{getDocumentTypeLabel(__, type) ?? type}
|
{getDocumentTypeLabel(__, type) ?? type}
|
||||||
</Option>
|
</Option>
|
||||||
@@ -174,140 +185,140 @@ export function DocumentList(props: {
|
|||||||
</div>
|
</div>
|
||||||
<div className={isPending ? "opacity-50 pointer-events-none transition-opacity" : ""}>
|
<div className={isPending ? "opacity-50 pointer-events-none transition-opacity" : ""}>
|
||||||
{documents.length > 0
|
{documents.length > 0
|
||||||
? (
|
? (
|
||||||
<SortableTable
|
<SortableTable
|
||||||
{...pagination}
|
{...pagination}
|
||||||
refetch={pagination.refetch as ComponentProps<typeof SortableTable>["refetch"]}
|
refetch={pagination.refetch as ComponentProps<typeof SortableTable>["refetch"]}
|
||||||
>
|
>
|
||||||
<Thead>
|
<Thead>
|
||||||
{selection.length === 0
|
{selection.length === 0
|
||||||
? (
|
? (
|
||||||
<Tr>
|
<Tr>
|
||||||
<Th className="w-18">
|
<Th className="w-18">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={
|
checked={
|
||||||
selection.length === documents.length
|
selection.length === documents.length
|
||||||
&& documents.length > 0
|
&& documents.length > 0
|
||||||
}
|
}
|
||||||
onChange={() => reset(documents.map(d => d.id))}
|
onChange={() => reset(documents.map(d => d.id))}
|
||||||
/>
|
/>
|
||||||
</Th>
|
</Th>
|
||||||
<SortableTh field="TITLE" className="min-w-0" onOrderChange={handleOrderChange}>
|
<SortableTh field="TITLE" className="min-w-0" onOrderChange={handleOrderChange}>
|
||||||
{__("Name")}
|
{__("Name")}
|
||||||
</SortableTh>
|
</SortableTh>
|
||||||
<Th className="w-24">{__("Status")}</Th>
|
<Th className="w-24">{__("Status")}</Th>
|
||||||
<Th className="w-20">{__("Version")}</Th>
|
<Th className="w-20">{__("Version")}</Th>
|
||||||
<SortableTh field="DOCUMENT_TYPE" className="w-28" onOrderChange={handleOrderChange}>
|
<SortableTh field="DOCUMENT_TYPE" className="w-28" onOrderChange={handleOrderChange}>
|
||||||
{__("Type")}
|
{__("Type")}
|
||||||
</SortableTh>
|
</SortableTh>
|
||||||
<Th className="w-32">{__("Classification")}</Th>
|
<Th className="w-32">{__("Classification")}</Th>
|
||||||
<Th className="w-60">{__("Approvers")}</Th>
|
<Th className="w-60">{__("Approvers")}</Th>
|
||||||
<Th className="w-60">{__("Last update")}</Th>
|
<Th className="w-60">{__("Last update")}</Th>
|
||||||
<Th className="w-20">{__("Signatures")}</Th>
|
<Th className="w-20">{__("Signatures")}</Th>
|
||||||
{hasAnyAction && <Th className="w-18"></Th>}
|
{hasAnyAction && <Th className="w-18"></Th>}
|
||||||
</Tr>
|
</Tr>
|
||||||
)
|
)
|
||||||
: (
|
: (
|
||||||
<Tr>
|
<Tr>
|
||||||
<Th colspan={10} compact>
|
<Th colspan={10} compact>
|
||||||
<div className="flex justify-between items-center h-8">
|
<div className="flex justify-between items-center h-8">
|
||||||
<div className="flex gap-2 items-center">
|
<div className="flex gap-2 items-center">
|
||||||
{sprintf(__("%s documents selected"), selection.length)}
|
{sprintf(__("%s documents selected"), selection.length)}
|
||||||
{" "}
|
{" "}
|
||||||
-
|
-
|
||||||
<button
|
<button
|
||||||
onClick={clear}
|
onClick={clear}
|
||||||
className="flex gap-1 items-center hover:text-txt-primary"
|
className="flex gap-1 items-center hover:text-txt-primary"
|
||||||
>
|
>
|
||||||
<IconCrossLargeX size={12} />
|
<IconCrossLargeX size={12} />
|
||||||
{__("Clear selection")}
|
{__("Clear selection")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2 items-center">
|
<div className="flex gap-2 items-center">
|
||||||
{canUpdateAny && (
|
{canUpdateAny && (
|
||||||
<PublishDocumentsDialog
|
<PublishDocumentsDialog
|
||||||
documentIds={selection}
|
documentIds={selection}
|
||||||
onSave={clear}
|
onSave={clear}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
icon={IconCheckmark1}
|
icon={IconCheckmark1}
|
||||||
className="py-0.5 px-2 text-xs h-6 min-h-6"
|
className="py-0.5 px-2 text-xs h-6 min-h-6"
|
||||||
>
|
>
|
||||||
{__("Publish")}
|
{__("Publish")}
|
||||||
</Button>
|
</Button>
|
||||||
</PublishDocumentsDialog>
|
</PublishDocumentsDialog>
|
||||||
)}
|
)}
|
||||||
{canRequestAnySignatures && (
|
{canRequestAnySignatures && (
|
||||||
<SignatureDocumentsDialog
|
<SignatureDocumentsDialog
|
||||||
documentIds={selection}
|
documentIds={selection}
|
||||||
onSave={clear}
|
onSave={clear}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
icon={IconSignature}
|
icon={IconSignature}
|
||||||
className="py-0.5 px-2 text-xs h-6 min-h-6"
|
className="py-0.5 px-2 text-xs h-6 min-h-6"
|
||||||
>
|
>
|
||||||
{__("Request signature")}
|
{__("Request signature")}
|
||||||
</Button>
|
</Button>
|
||||||
</SignatureDocumentsDialog>
|
</SignatureDocumentsDialog>
|
||||||
)}
|
)}
|
||||||
<BulkExportDialog
|
<BulkExportDialog
|
||||||
ref={bulkExportDialogRef}
|
ref={bulkExportDialogRef}
|
||||||
onExport={handleBulkExport}
|
onExport={handleBulkExport}
|
||||||
isLoading={isBulkExporting}
|
isLoading={isBulkExporting}
|
||||||
defaultEmail={defaultEmail}
|
defaultEmail={defaultEmail}
|
||||||
selectedCount={selection.length}
|
selectedCount={selection.length}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
icon={IconArrowDown}
|
icon={IconArrowDown}
|
||||||
className="py-0.5 px-2 text-xs h-6 min-h-6"
|
className="py-0.5 px-2 text-xs h-6 min-h-6"
|
||||||
>
|
>
|
||||||
{__("Export")}
|
{__("Export")}
|
||||||
</Button>
|
</Button>
|
||||||
</BulkExportDialog>
|
</BulkExportDialog>
|
||||||
{canDeleteAny && (
|
{canDeleteAny && (
|
||||||
<Button
|
<Button
|
||||||
variant="danger"
|
variant="danger"
|
||||||
icon={IconTrashCan}
|
icon={IconTrashCan}
|
||||||
onClick={handleBulkDelete}
|
onClick={handleBulkDelete}
|
||||||
className="py-0.5 px-2 text-xs h-6 min-h-6"
|
className="py-0.5 px-2 text-xs h-6 min-h-6"
|
||||||
>
|
>
|
||||||
{__("Delete")}
|
{__("Delete")}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Th>
|
</Th>
|
||||||
</Tr>
|
</Tr>
|
||||||
)}
|
)}
|
||||||
</Thead>
|
</Thead>
|
||||||
<Tbody>
|
<Tbody>
|
||||||
{documents.map(document => (
|
{documents.map(document => (
|
||||||
<DocumentListItem
|
<DocumentListItem
|
||||||
checked={selection.includes(document.id)}
|
checked={selection.includes(document.id)}
|
||||||
onCheck={() => toggle(document.id)}
|
onCheck={() => toggle(document.id)}
|
||||||
key={document.id}
|
key={document.id}
|
||||||
fragmentRef={document}
|
fragmentRef={document}
|
||||||
connectionId={connectionId}
|
connectionId={connectionId}
|
||||||
hasAnyAction={hasAnyAction}
|
hasAnyAction={hasAnyAction}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Tbody>
|
</Tbody>
|
||||||
</SortableTable>
|
</SortableTable>
|
||||||
)
|
)
|
||||||
: (
|
: (
|
||||||
<Card padded>
|
<Card padded>
|
||||||
<div className="text-center py-12">
|
<div className="text-center py-12">
|
||||||
<h3 className="text-lg font-semibold mb-2">
|
<h3 className="text-lg font-semibold mb-2">
|
||||||
{__("No documents yet")}
|
{__("No documents yet")}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-txt-tertiary mb-4">
|
<p className="text-txt-tertiary mb-4">
|
||||||
{__("Create your first document to get started.")}
|
{__("Create your first document to get started.")}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user