Refactor compliance page documents page

Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
Émile Ré
2026-01-28 16:52:17 +04:00
parent 3e3c3a8ebb
commit 36a936fcf4
17 changed files with 584 additions and 787 deletions

View File

@@ -1,104 +0,0 @@
import { sprintf } from "@probo/helpers";
import { useTranslate } from "@probo/i18n";
import { Badge, Button, Card } from "@probo/ui";
import type { TrustCenterGraphQuery$data } from "#/__generated__/core/TrustCenterGraphQuery.graphql";
type Props = {
canUpdate: boolean;
organizationId: string;
slackConnections: NonNullable<
TrustCenterGraphQuery$data["organization"]["slackConnections"]
>["edges"][number]["node"][];
};
export function SlackConnections({
canUpdate,
organizationId,
slackConnections: connectedSlackConnections,
}: Props) {
const { __, dateTimeFormat } = useTranslate();
const slackConnectionDefinitions = [
{
id: "SLACK",
name: "Slack",
protocol: "OAUTH2",
description: __("Manage your trust center access with slack"),
},
];
const slackConnections = slackConnectionDefinitions.map((def) => {
const connected = connectedSlackConnections.find(c => c.id);
return {
...def,
createdAt: connected?.createdAt,
channel: connected?.channel,
channelId: connected?.channelId,
};
});
const getUrl = (provider: string) => {
const baseUrl = import.meta.env.VITE_API_URL || window.location.origin;
const url = new URL("/api/console/v1/connectors/initiate", baseUrl);
url.searchParams.append("organization_id", organizationId);
url.searchParams.append("provider", provider);
const trustCenterUrl = `/organizations/${organizationId}/trust-center`;
url.searchParams.append("continue", trustCenterUrl);
const finalUrl = url.toString();
return finalUrl;
};
return (
<div className="space-y-2">
{slackConnections.map(slackConnection => (
<Card
key={slackConnection.id}
padded
className="flex items-center gap-3"
>
<div>
<img src={`/${slackConnection.id.toLowerCase()}.png`} alt="" />
</div>
<div className="mr-auto">
<h3 className="text-base font-semibold">{slackConnection.name}</h3>
<p className="text-sm text-txt-tertiary">
{slackConnection.createdAt
? (
<>
{sprintf(
__("Connected on %s"),
dateTimeFormat(slackConnection.createdAt),
)}
{slackConnection.channel && (
<>
{" • "}
{sprintf(__("Channel: %s"), slackConnection.channel)}
</>
)}
</>
)
: (
slackConnection.description
)}
</p>
</div>
{slackConnection.createdAt
? (
<div>
<Badge variant="success" size="md">
{__("Connected")}
</Badge>
</div>
)
: (
canUpdate && (
<Button variant="secondary" asChild>
<a href={getUrl(slackConnection.id)}>{__("Connect")}</a>
</Button>
)
)}
</Card>
))}
</div>
);
}

View File

@@ -1,188 +0,0 @@
import { getTrustCenterVisibilityOptions, sprintf } from "@probo/helpers";
import { useTranslate } from "@probo/i18n";
import {
Badge,
Button,
DocumentTypeBadge,
DocumentVersionBadge,
Field,
IconChevronDown,
Option,
Table,
Tbody,
Td,
Th,
Thead,
Tr,
} from "@probo/ui";
import { useCallback, useMemo, useState } from "react";
import { useFragment } from "react-relay";
import { graphql } from "relay-runtime";
import type { TrustCenterDocumentsCardFragment$key } from "#/__generated__/core/TrustCenterDocumentsCardFragment.graphql";
import { useOrganizationId } from "#/hooks/useOrganizationId";
const trustCenterDocumentFragment = graphql`
fragment TrustCenterDocumentsCardFragment on Document {
id
title
createdAt
documentType
trustCenterVisibility
versions(first: 1) {
edges {
node {
id
status
}
}
}
}
`;
type Mutation<Params> = (p: {
variables: {
input: {
id: string;
trustCenterVisibility: "NONE" | "PRIVATE" | "PUBLIC";
} & Params;
};
}) => Promise<void>;
type Props<Params> = {
documents: TrustCenterDocumentsCardFragment$key[];
params: Params;
disabled?: boolean;
onChangeVisibility: Mutation<Params>;
canUpdate: boolean;
};
export function TrustCenterDocumentsCard<Params>(props: Props<Params>) {
const { __ } = useTranslate();
const [limit, setLimit] = useState<number | null>(100);
const documents = useMemo(() => {
return limit ? props.documents.slice(0, limit) : props.documents;
}, [props.documents, limit]);
const showMoreButton = limit !== null && props.documents.length > limit;
const onChangeVisibility = async (
documentId: string,
trustCenterVisibility: "NONE" | "PRIVATE" | "PUBLIC",
) => {
await props.onChangeVisibility({
variables: {
input: {
id: documentId,
trustCenterVisibility,
...props.params,
},
},
});
};
return (
<div className="space-y-[10px]">
<Table>
<Thead>
<Tr>
<Th>{__("Name")}</Th>
<Th>{__("Type")}</Th>
<Th>{__("State")}</Th>
<Th>{__("Visibility")}</Th>
</Tr>
</Thead>
<Tbody>
{documents.length === 0 && (
<Tr>
<Td colSpan={5} className="text-center text-txt-secondary">
{__("No documents available")}
</Td>
</Tr>
)}
{documents.map((document, index) => (
<DocumentRow
key={index}
documentFragmentRef={document}
onChangeVisibility={onChangeVisibility}
disabled={props.disabled}
canUpdate={props.canUpdate}
/>
))}
</Tbody>
</Table>
{showMoreButton && (
<Button
variant="tertiary"
onClick={() => setLimit(null)}
className="mt-3 mx-auto"
icon={IconChevronDown}
>
{sprintf(__("Show %s more"), props.documents.length - limit)}
</Button>
)}
</div>
);
}
function DocumentRow(props: {
documentFragmentRef: TrustCenterDocumentsCardFragment$key;
onChangeVisibility: (
documentId: string,
trustCenterVisibility: "NONE" | "PRIVATE" | "PUBLIC",
) => Promise<void>;
disabled?: boolean;
canUpdate: boolean;
}) {
const { documentFragmentRef, onChangeVisibility, disabled, canUpdate }
= props;
const document = useFragment(
trustCenterDocumentFragment,
documentFragmentRef,
);
const organizationId = useOrganizationId();
const { __ } = useTranslate();
const handleValueChange = useCallback(
async (value: string) => {
const stringValue = typeof value === "string" ? value : "";
const typedValue = stringValue as "NONE" | "PRIVATE" | "PUBLIC";
await onChangeVisibility(document.id, typedValue);
},
[document.id, onChangeVisibility],
);
const visibilityOptions = getTrustCenterVisibilityOptions(__);
return (
<Tr to={`/organizations/${organizationId}/documents/${document.id}`}>
<Td>
<div className="flex gap-4 items-center">{document.title}</div>
</Td>
<Td>
<DocumentTypeBadge type={document.documentType} />
</Td>
<Td>
<DocumentVersionBadge
state={document.versions?.edges?.[0]?.node?.status}
/>
</Td>
<Td noLink width={130} className="pr-0">
<Field
type="select"
value={document.trustCenterVisibility}
onValueChange={value => void handleValueChange(value)}
disabled={disabled || !canUpdate}
className="w-[105px]"
>
{visibilityOptions.map(option => (
<Option key={option.value} value={option.value}>
<div className="flex items-center justify-between w-full">
<Badge variant={option.variant}>{option.label}</Badge>
</div>
</Option>
))}
</Field>
</Td>
</Tr>
);
}