Add countries and category to vendors

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
Sacha Al Himdani
2025-09-12 15:32:06 +02:00
parent 41512a7a89
commit a2dc85f853
23 changed files with 3781 additions and 16 deletions

View File

@@ -51,6 +51,7 @@ export function CreateVendorDialog({
serviceLevelAgreementUrl: vendor.serviceLevelAgreementUrl,
dataProcessingAgreementUrl: vendor.dataProcessingAgreementUrl,
certifications: vendor.certifications,
countries: vendor.countries,
securityPageUrl: vendor.securityPageUrl,
trustPageUrl: vendor.trustPageUrl,
statusPageUrl: vendor.statusPageUrl,

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<a0f6e47aa4001c2a99c02b7eabe5c99a>>
* @generated SignedSource<<6db92ed13cdca3a852d1b7b9c113840e>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -218,6 +218,13 @@ return {
"name": "description",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "category",
"storageKey": null
},
{
"alias": null,
"args": null,
@@ -274,6 +281,13 @@ return {
"name": "certifications",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "countries",
"storageKey": null
},
{
"alias": null,
"args": null,
@@ -528,12 +542,12 @@ return {
]
},
"params": {
"cacheID": "dbe0c3ef0f5d4d36436224603629d30a",
"cacheID": "025f271f1dca46c6f052f0d59a4315cc",
"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 statusPageUrl\n termsOfServiceUrl\n privacyPolicyUrl\n serviceLevelAgreementUrl\n dataProcessingAgreementUrl\n websiteUrl\n legalName\n headquarterAddress\n certifications\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 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"
}
};
})();

View File

@@ -1,8 +1,10 @@
import { useTranslate } from "@probo/i18n";
import { useVendorForm } from "/hooks/forms/useVendorForm";
import { useOutletContext, useParams } from "react-router";
import { Button, Card, Field, Input, IconPlusLarge, IconTrashCan, IconPencil } from "@probo/ui";
import { Button, Card, Field, Input, IconPlusLarge, IconTrashCan, IconPencil, Option } from "@probo/ui";
import { PeopleSelectField } from "/components/form/PeopleSelectField";
import { ControlledField } from "/components/form/ControlledField";
import { CountriesField } from "/components/form/CountriesField";
import { useOrganizationId } from "/hooks/useOrganizationId";
import { useMemo } from "react";
import { usePageTitle } from "@probo/hooks";
@@ -17,6 +19,7 @@ import { EditDataPrivacyAgreementDialog } from "../dialogs/EditDataPrivacyAgreem
import type { useVendorFormFragment$key } from "/hooks/forms/__generated__/useVendorFormFragment.graphql";
import type { VendorOverviewTabBusinessAssociateAgreementFragment$key } from "./__generated__/VendorOverviewTabBusinessAssociateAgreementFragment.graphql";
import type { VendorOverviewTabDataPrivacyAgreementFragment$key } from "./__generated__/VendorOverviewTabDataPrivacyAgreementFragment.graphql";
import type { VendorCategory } from "@probo/vendors";
const vendorBusinessAssociateAgreementFragment = graphql`
fragment VendorOverviewTabBusinessAssociateAgreementFragment on Vendor {
@@ -54,6 +57,31 @@ export default function VendorOverviewTab() {
}>();
const { __ } = useTranslate();
const vendorCategories: { value: VendorCategory; label: string }[] = [
{ value: "ANALYTICS", label: __("Analytics") },
{ value: "CLOUD_MONITORING", label: __("Cloud Monitoring") },
{ value: "CLOUD_PROVIDER", label: __("Cloud Provider") },
{ value: "COLLABORATION", label: __("Collaboration") },
{ value: "CUSTOMER_SUPPORT", label: __("Customer Support") },
{ value: "DATA_STORAGE_AND_PROCESSING", label: __("Data Storage and Processing") },
{ value: "DOCUMENT_MANAGEMENT", label: __("Document Management") },
{ value: "EMPLOYEE_MANAGEMENT", label: __("Employee Management") },
{ value: "ENGINEERING", label: __("Engineering") },
{ value: "FINANCE", label: __("Finance") },
{ value: "IDENTITY_PROVIDER", label: __("Identity Provider") },
{ value: "IT", label: __("IT") },
{ value: "MARKETING", label: __("Marketing") },
{ value: "OFFICE_OPERATIONS", label: __("Office Operations") },
{ value: "OTHER", label: __("Other") },
{ value: "PASSWORD_MANAGEMENT", label: __("Password Management") },
{ value: "PRODUCT_AND_DESIGN", label: __("Product and Design") },
{ value: "PROFESSIONAL_SERVICES", label: __("Professional Services") },
{ value: "RECRUITING", label: __("Recruiting") },
{ value: "SALES", label: __("Sales") },
{ value: "SECURITY", label: __("Security") },
{ value: "VERSION_CONTROL", label: __("Version Control") },
];
const organizationId = useOrganizationId();
const { snapshotId } = useParams<{ snapshotId?: string }>();
const isSnapshotMode = Boolean(snapshotId);
@@ -119,6 +147,21 @@ export default function VendorOverviewTab() {
error={errors.description?.message}
disabled={isSubmitting || isSnapshotMode}
/>
<ControlledField
control={control}
name="category"
type="select"
label={__("Category")}
placeholder={__("Select a category")}
error={errors.category?.message}
disabled={isSubmitting || isSnapshotMode}
>
{vendorCategories.map((category) => (
<Option key={category.value} value={category.value}>
{category.label}
</Option>
))}
</ControlledField>
<Field
{...register("legalName")}
label={__("Legal name")}
@@ -143,6 +186,17 @@ export default function VendorOverviewTab() {
</Card>
</div>
<div className="space-y-4">
<h2 className="text-base font-medium">{__("Countries")}</h2>
<Card padded>
<CountriesField
control={control}
name="countries"
disabled={isSubmitting || isSnapshotMode}
/>
</Card>
</div>
{/* Ownership */}
<div className="space-y-4">
<h2 className="text-base font-medium">{__("Ownership details")}</h2>