Decouple users from people

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
Sacha Al Himdani
2025-09-22 15:02:08 +02:00
parent 104330d3fd
commit 533822b60d
35 changed files with 296 additions and 872 deletions

View File

@@ -90,9 +90,6 @@ const documentFragment = graphql`
publishedAt
version
updatedAt
publishedBy {
fullName
}
signatures(first: 100)
@connection(key: "DocumentDetailPage_signatures") {
__id
@@ -707,9 +704,6 @@ function VersionItem({
</div>
<div className="flex-1 space-y-[2px] overflow-hidden">
<div className="flex items-center gap-2 overflow-hidden">
<div className="text-sm text-txt-secondary whitespace-nowrap overflow-hidden text-ellipsis">
{version.publishedBy?.fullName ?? document.owner.fullName}
</div>
{version.status === "DRAFT" && (
<Badge variant="neutral" size="sm">
{__("Draft")}

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<8db0c7dcd7ff31c01ec5facd5a3b598a>>
* @generated SignedSource<<9bec5530e7a90ab3fb9f21c5c2d92286>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -31,9 +31,6 @@ export type DocumentDetailPageDocumentFragment$data = {
readonly content: string;
readonly id: string;
readonly publishedAt: any | null | undefined;
readonly publishedBy: {
readonly fullName: string;
} | null | undefined;
readonly signatures: {
readonly __id: string;
readonly edges: ReadonlyArray<{
@@ -73,24 +70,17 @@ v1 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "fullName",
"name": "__typename",
"storageKey": null
},
v2 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "__typename",
"storageKey": null
},
v3 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "cursor",
"storageKey": null
},
v4 = {
v3 = {
"alias": null,
"args": null,
"concreteType": "PageInfo",
@@ -115,7 +105,7 @@ v4 = {
],
"storageKey": null
},
v5 = {
v4 = {
"kind": "ClientExtension",
"selections": [
{
@@ -174,7 +164,13 @@ return {
"plural": false,
"selections": [
(v0/*: any*/),
(v1/*: any*/)
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "fullName",
"storageKey": null
}
],
"storageKey": null
},
@@ -267,18 +263,6 @@ return {
"name": "updatedAt",
"storageKey": null
},
{
"alias": null,
"args": null,
"concreteType": "People",
"kind": "LinkedField",
"name": "publishedBy",
"plural": false,
"selections": [
(v1/*: any*/)
],
"storageKey": null
},
{
"alias": "signatures",
"args": null,
@@ -328,29 +312,29 @@ return {
"kind": "FragmentSpread",
"name": "DocumentSignaturesTab_signature"
},
(v2/*: any*/)
(v1/*: any*/)
],
"storageKey": null
},
(v3/*: any*/)
(v2/*: any*/)
],
"storageKey": null
},
(v4/*: any*/),
(v5/*: any*/)
(v3/*: any*/),
(v4/*: any*/)
],
"storageKey": null
},
(v2/*: any*/)
(v1/*: any*/)
],
"storageKey": null
},
(v3/*: any*/)
(v2/*: any*/)
],
"storageKey": null
},
(v4/*: any*/),
(v5/*: any*/)
(v3/*: any*/),
(v4/*: any*/)
],
"storageKey": null
}
@@ -360,6 +344,6 @@ return {
};
})();
(node as any).hash = "1523ddad229ae2d6bf94d30de5579ae6";
(node as any).hash = "1d06e505efa74bb0b564fcbfc57ce8c9";
export default node;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<e02cdda6c1c24ac700a79ed236c6dfdc>>
* @generated SignedSource<<aa94f8d2cc4c6a5c29de3029d5e6d2ca>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -12,7 +12,6 @@ import { ConcreteRequest } from 'relay-runtime';
export type DocumentType = "ISMS" | "OTHER" | "POLICY";
export type UpdateDocumentInput = {
content?: string | null | undefined;
createdBy?: string | null | undefined;
documentType?: DocumentType | null | undefined;
id: string;
ownerId?: string | null | undefined;

View File

@@ -132,7 +132,7 @@ export default function VendorDetailPage(props: Props) {
</TabLink>
</Tabs>
<Outlet context={{ vendor, peopleId: data.viewer.user.people!.id }} />
<Outlet context={{ vendor }} />
</div>
);
}

View File

@@ -134,8 +134,8 @@ function VendorRow({
</div>
</Td>
<Td>
{latestAssessment?.assessedAt
? formatDate(latestAssessment.assessedAt)
{latestAssessment?.createdAt
? formatDate(latestAssessment.createdAt)
: __("Not assessed")}
</Td>
<Td>

View File

@@ -21,7 +21,6 @@ type Props = {
children: ReactNode;
connection: string;
vendorId: string;
peopleId: string;
};
const createRiskAssessmentMutation = graphql`
@@ -52,7 +51,6 @@ export function CreateRiskAssessmentDialog({
children,
connection,
vendorId,
peopleId,
}: Props) {
const { __ } = useTranslate();
@@ -79,7 +77,6 @@ export function CreateRiskAssessmentDialog({
input: {
...data,
vendorId,
assessedBy: peopleId,
expiresAt: nextYear.toISOString(),
},
connections: [connection!],

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<f877423fd1b767f837337e1b8a917e51>>
* @generated SignedSource<<2cd3f372cc571cec881b9762e381f1d5>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -13,7 +13,6 @@ import { FragmentRefs } from "relay-runtime";
export type BusinessImpact = "CRITICAL" | "HIGH" | "LOW" | "MEDIUM";
export type DataSensitivity = "CRITICAL" | "HIGH" | "LOW" | "MEDIUM" | "NONE";
export type CreateVendorRiskAssessmentInput = {
assessedBy: string;
businessImpact: BusinessImpact;
dataSensitivity: DataSensitivity;
expiresAt: any;
@@ -55,14 +54,7 @@ v2 = [
"name": "input",
"variableName": "input"
}
],
v3 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "id",
"storageKey": null
};
];
return {
"fragment": {
"argumentDefinitions": [
@@ -148,31 +140,18 @@ return {
"name": "node",
"plural": false,
"selections": [
(v3/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "assessedAt",
"name": "id",
"storageKey": null
},
{
"alias": null,
"args": null,
"concreteType": "People",
"kind": "LinkedField",
"name": "assessedBy",
"plural": false,
"selections": [
(v3/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "fullName",
"storageKey": null
}
],
"kind": "ScalarField",
"name": "createdAt",
"storageKey": null
},
{
@@ -231,12 +210,12 @@ return {
]
},
"params": {
"cacheID": "c55703ef9adb6c6aabf79aafe6cbeb89",
"cacheID": "1bd928ac46edc24d7b7cda3e5d7e2cb2",
"id": null,
"metadata": {},
"name": "CreateRiskAssessmentDialogMutation",
"operationKind": "mutation",
"text": "mutation CreateRiskAssessmentDialogMutation(\n $input: CreateVendorRiskAssessmentInput!\n) {\n createVendorRiskAssessment(input: $input) {\n vendorRiskAssessmentEdge {\n node {\n ...VendorRiskAssessmentTabFragment_assessment\n id\n }\n }\n }\n}\n\nfragment VendorRiskAssessmentTabFragment_assessment on VendorRiskAssessment {\n id\n assessedAt\n assessedBy {\n id\n fullName\n }\n expiresAt\n dataSensitivity\n businessImpact\n notes\n}\n"
"text": "mutation CreateRiskAssessmentDialogMutation(\n $input: CreateVendorRiskAssessmentInput!\n) {\n createVendorRiskAssessment(input: $input) {\n vendorRiskAssessmentEdge {\n node {\n ...VendorRiskAssessmentTabFragment_assessment\n id\n }\n }\n }\n}\n\nfragment VendorRiskAssessmentTabFragment_assessment on VendorRiskAssessment {\n id\n createdAt\n expiresAt\n dataSensitivity\n businessImpact\n notes\n}\n"
}
};
})();

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<6db92ed13cdca3a852d1b7b9c113840e>>
* @generated SignedSource<<22e7411298e2df9b5f5eae17bb0a510f>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -447,26 +447,7 @@ return {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "assessedAt",
"storageKey": null
},
{
"alias": null,
"args": null,
"concreteType": "People",
"kind": "LinkedField",
"name": "assessedBy",
"plural": false,
"selections": [
(v2/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "fullName",
"storageKey": null
}
],
"name": "createdAt",
"storageKey": null
},
{
@@ -542,12 +523,12 @@ return {
]
},
"params": {
"cacheID": "025f271f1dca46c6f052f0d59a4315cc",
"cacheID": "0cb0f301c3022d933fd9ee0d6319d3e4",
"id": null,
"metadata": {},
"name": "ImportAssessmentDialogMutation",
"operationKind": "mutation",
"text": "mutation ImportAssessmentDialogMutation(\n $input: AssessVendorInput!\n) {\n assessVendor(input: $input) {\n vendor {\n id\n name\n websiteUrl\n ...useVendorFormFragment\n ...VendorComplianceTabFragment\n ...VendorRiskAssessmentTabFragment\n }\n }\n}\n\nfragment VendorComplianceTabFragment on Vendor {\n complianceReports(first: 50) {\n edges {\n node {\n id\n ...VendorComplianceTabFragment_report\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n\nfragment VendorComplianceTabFragment_report on VendorComplianceReport {\n id\n reportDate\n validUntil\n reportName\n fileUrl\n fileSize\n}\n\nfragment VendorRiskAssessmentTabFragment on Vendor {\n id\n riskAssessments(first: 50) {\n edges {\n node {\n id\n ...VendorRiskAssessmentTabFragment_assessment\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n endCursor\n hasPreviousPage\n startCursor\n }\n }\n}\n\nfragment VendorRiskAssessmentTabFragment_assessment on VendorRiskAssessment {\n id\n assessedAt\n assessedBy {\n id\n fullName\n }\n expiresAt\n dataSensitivity\n businessImpact\n notes\n}\n\nfragment useVendorFormFragment on Vendor {\n id\n name\n description\n category\n statusPageUrl\n termsOfServiceUrl\n privacyPolicyUrl\n serviceLevelAgreementUrl\n dataProcessingAgreementUrl\n websiteUrl\n legalName\n headquarterAddress\n certifications\n countries\n securityPageUrl\n trustPageUrl\n businessOwner {\n id\n }\n securityOwner {\n id\n }\n}\n"
"text": "mutation ImportAssessmentDialogMutation(\n $input: AssessVendorInput!\n) {\n assessVendor(input: $input) {\n vendor {\n id\n name\n websiteUrl\n ...useVendorFormFragment\n ...VendorComplianceTabFragment\n ...VendorRiskAssessmentTabFragment\n }\n }\n}\n\nfragment VendorComplianceTabFragment on Vendor {\n complianceReports(first: 50) {\n edges {\n node {\n id\n ...VendorComplianceTabFragment_report\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n\nfragment VendorComplianceTabFragment_report on VendorComplianceReport {\n id\n reportDate\n validUntil\n reportName\n fileUrl\n fileSize\n}\n\nfragment VendorRiskAssessmentTabFragment on Vendor {\n id\n riskAssessments(first: 50) {\n edges {\n node {\n id\n ...VendorRiskAssessmentTabFragment_assessment\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n endCursor\n hasPreviousPage\n startCursor\n }\n }\n}\n\nfragment VendorRiskAssessmentTabFragment_assessment on VendorRiskAssessment {\n id\n createdAt\n expiresAt\n dataSensitivity\n businessImpact\n notes\n}\n\nfragment useVendorFormFragment on Vendor {\n id\n name\n description\n category\n statusPageUrl\n termsOfServiceUrl\n privacyPolicyUrl\n serviceLevelAgreementUrl\n dataProcessingAgreementUrl\n websiteUrl\n legalName\n headquarterAddress\n certifications\n countries\n securityPageUrl\n trustPageUrl\n businessOwner {\n id\n }\n securityOwner {\n id\n }\n}\n"
}
};
})();

View File

@@ -15,6 +15,7 @@ import {
Badge,
TrButton,
} from "@probo/ui";
import { formatDate } from "@probo/helpers";
import { useFragment, useRefetchableFragment } from "react-relay";
import type { VendorRiskAssessmentTabFragment_assessment$key } from "./__generated__/VendorRiskAssessmentTabFragment_assessment.graphql";
import { SortableTable, SortableTh } from "/components/SortableTable";
@@ -59,11 +60,7 @@ const riskAssessmentsFragment = graphql`
const riskAssessmentFragment = graphql`
fragment VendorRiskAssessmentTabFragment_assessment on VendorRiskAssessment {
id
assessedAt
assessedBy {
id
fullName
}
createdAt
expiresAt
dataSensitivity
businessImpact
@@ -72,9 +69,8 @@ const riskAssessmentFragment = graphql`
`;
export default function VendorRiskAssessmentTab() {
const { vendor, peopleId } = useOutletContext<{
const { vendor } = useOutletContext<{
vendor: VendorRiskAssessmentTabFragment$key & { name: string; id: string };
peopleId: string;
}>();
const [data, refetch] = useRefetchableFragment(
riskAssessmentsFragment,
@@ -96,7 +92,6 @@ export default function VendorRiskAssessmentTab() {
<CreateRiskAssessmentDialog
vendorId={vendor.id}
connection={data.riskAssessments.__id}
peopleId={peopleId}
>
<Button icon={IconPlusLarge} variant="secondary">
{__("Add Risk Assessment")}
@@ -114,7 +109,7 @@ export default function VendorRiskAssessmentTab() {
<SortableTable refetch={refetch}>
<Thead>
<Tr>
<SortableTh field="ASSESSED_AT">{__("Assessed By")}</SortableTh>
<SortableTh field="CREATED_AT">{__("Created At")}</SortableTh>
<SortableTh field="EXPIRES_AT">{__("Expires")}</SortableTh>
<Th>{__("Data sensitivity")}</Th>
<Th>{__("Business impact")}</Th>
@@ -125,7 +120,6 @@ export default function VendorRiskAssessmentTab() {
<CreateRiskAssessmentDialog
vendorId={vendor.id}
connection={data.riskAssessments.__id}
peopleId={peopleId}
>
<TrButton colspan={5} onClick={() => {}}>
{__("Add Risk Assessment")}
@@ -178,9 +172,8 @@ function AssessmentRow(props: AssessmentRowProps) {
onClick={() => props.onClick(assessment.id)}
>
<Td>
{assessment.assessedBy?.fullName}
<span className="text-xs text-txt-secondary ml-1">
({relativeDateFormat(assessment.assessedAt)})
{formatDate(assessment.createdAt)}
</span>
</Td>
<Td>

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<68ddff15cd65f5367f7e9ae6b3c38f34>>
* @generated SignedSource<<b6963cdf0e4c8a5c6bb293cb53f8d404>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -13,12 +13,8 @@ export type BusinessImpact = "CRITICAL" | "HIGH" | "LOW" | "MEDIUM";
export type DataSensitivity = "CRITICAL" | "HIGH" | "LOW" | "MEDIUM" | "NONE";
import { FragmentRefs } from "relay-runtime";
export type VendorRiskAssessmentTabFragment_assessment$data = {
readonly assessedAt: any;
readonly assessedBy: {
readonly fullName: string;
readonly id: string;
};
readonly businessImpact: BusinessImpact;
readonly createdAt: any;
readonly dataSensitivity: DataSensitivity;
readonly expiresAt: any;
readonly id: string;
@@ -30,45 +26,24 @@ export type VendorRiskAssessmentTabFragment_assessment$key = {
readonly " $fragmentSpreads": FragmentRefs<"VendorRiskAssessmentTabFragment_assessment">;
};
const node: ReaderFragment = (function(){
var v0 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "id",
"storageKey": null
};
return {
const node: ReaderFragment = {
"argumentDefinitions": [],
"kind": "Fragment",
"metadata": null,
"name": "VendorRiskAssessmentTabFragment_assessment",
"selections": [
(v0/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "assessedAt",
"name": "id",
"storageKey": null
},
{
"alias": null,
"args": null,
"concreteType": "People",
"kind": "LinkedField",
"name": "assessedBy",
"plural": false,
"selections": [
(v0/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "fullName",
"storageKey": null
}
],
"kind": "ScalarField",
"name": "createdAt",
"storageKey": null
},
{
@@ -103,8 +78,7 @@ return {
"type": "VendorRiskAssessment",
"abstractKey": null
};
})();
(node as any).hash = "08d6de198077f9b5985258466bbc33c0";
(node as any).hash = "533771f5fac9e732556929fa2b1d0dc5";
export default node;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<0bca6db2c1a834fd326761b3544d00ae>>
* @generated SignedSource<<121b4497478f4b5ff67fb8a9daab3774>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -11,7 +11,7 @@
import { ConcreteRequest } from 'relay-runtime';
import { FragmentRefs } from "relay-runtime";
export type OrderDirection = "ASC" | "DESC";
export type VendorRiskAssessmentOrderField = "ASSESSED_AT" | "CREATED_AT" | "EXPIRES_AT";
export type VendorRiskAssessmentOrderField = "CREATED_AT" | "EXPIRES_AT";
export type VendorRiskAssessmentOrder = {
direction: OrderDirection;
field: VendorRiskAssessmentOrderField;
@@ -216,26 +216,7 @@ return {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "assessedAt",
"storageKey": null
},
{
"alias": null,
"args": null,
"concreteType": "People",
"kind": "LinkedField",
"name": "assessedBy",
"plural": false,
"selections": [
(v12/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "fullName",
"storageKey": null
}
],
"name": "createdAt",
"storageKey": null
},
{
@@ -355,12 +336,12 @@ return {
]
},
"params": {
"cacheID": "26ab0b864c4e22a21c078f70ed7caf93",
"cacheID": "5bf6eff01e3ca53e8da78cba59316a0c",
"id": null,
"metadata": {},
"name": "VendorRiskAssessmentTabQuery",
"operationKind": "query",
"text": "query VendorRiskAssessmentTabQuery(\n $after: CursorKey = null\n $before: CursorKey = null\n $first: Int = 50\n $last: Int = null\n $order: VendorRiskAssessmentOrder = null\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...VendorRiskAssessmentTabFragment_16fISc\n id\n }\n}\n\nfragment VendorRiskAssessmentTabFragment_16fISc on Vendor {\n id\n riskAssessments(first: $first, after: $after, last: $last, before: $before, orderBy: $order) {\n edges {\n node {\n id\n ...VendorRiskAssessmentTabFragment_assessment\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n endCursor\n hasPreviousPage\n startCursor\n }\n }\n}\n\nfragment VendorRiskAssessmentTabFragment_assessment on VendorRiskAssessment {\n id\n assessedAt\n assessedBy {\n id\n fullName\n }\n expiresAt\n dataSensitivity\n businessImpact\n notes\n}\n"
"text": "query VendorRiskAssessmentTabQuery(\n $after: CursorKey = null\n $before: CursorKey = null\n $first: Int = 50\n $last: Int = null\n $order: VendorRiskAssessmentOrder = null\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...VendorRiskAssessmentTabFragment_16fISc\n id\n }\n}\n\nfragment VendorRiskAssessmentTabFragment_16fISc on Vendor {\n id\n riskAssessments(first: $first, after: $after, last: $last, before: $before, orderBy: $order) {\n edges {\n node {\n id\n ...VendorRiskAssessmentTabFragment_assessment\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n endCursor\n hasPreviousPage\n startCursor\n }\n }\n}\n\nfragment VendorRiskAssessmentTabFragment_assessment on VendorRiskAssessment {\n id\n createdAt\n expiresAt\n dataSensitivity\n businessImpact\n notes\n}\n"
}
};
})();