diff --git a/apps/console2/src/hooks/forms/useRiskForm.tsx b/apps/console2/src/hooks/forms/useRiskForm.tsx
index 8a5658172..9a8b022f4 100644
--- a/apps/console2/src/hooks/forms/useRiskForm.tsx
+++ b/apps/console2/src/hooks/forms/useRiskForm.tsx
@@ -34,13 +34,7 @@ export const riskSchema = z.object({
name: z.string(),
description: z.string(),
ownerId: z.string(),
- treatment: z.enum([
- "AVOIDED",
- "MITIGATED",
- "TRANSFERRED",
- "ACCEPTED",
- "%future added value",
- ]),
+ treatment: z.enum(["AVOIDED", "MITIGATED", "TRANSFERRED", "ACCEPTED"]),
inherentLikelihood: z.number({ coerce: true }).min(1).max(5),
inherentImpact: z.number({ coerce: true }).min(1).max(5),
residualLikelihood: z.number({ coerce: true }).min(1).max(5),
diff --git a/apps/console2/src/hooks/forms/useVendorForm.tsx b/apps/console2/src/hooks/forms/useVendorForm.tsx
index 7f0d36821..b063a1017 100644
--- a/apps/console2/src/hooks/forms/useVendorForm.tsx
+++ b/apps/console2/src/hooks/forms/useVendorForm.tsx
@@ -1,7 +1,7 @@
import { z } from "zod";
import { useFormWithSchema } from "../useFormWithSchema";
import { graphql } from "relay-runtime";
-import { useFragment, useMutation } from "react-relay";
+import { useFragment } from "react-relay";
import type { useVendorFormFragment$key } from "./__generated__/useVendorFormFragment.graphql";
import { useMutationWithToasts } from "../useMutationWithToasts";
import { useTranslate } from "@probo/i18n";
diff --git a/apps/console2/src/hooks/graph/VendorGraph.ts b/apps/console2/src/hooks/graph/VendorGraph.ts
index 643b7ca70..b013b91fd 100644
--- a/apps/console2/src/hooks/graph/VendorGraph.ts
+++ b/apps/console2/src/hooks/graph/VendorGraph.ts
@@ -131,6 +131,7 @@ export const vendorNodeQuery = graphql`
name
websiteUrl
...useVendorFormFragment
+ ...VendorComplianceTabFragment
}
}
}
diff --git a/apps/console2/src/hooks/graph/__generated__/VendorGraphNodeQuery.graphql.ts b/apps/console2/src/hooks/graph/__generated__/VendorGraphNodeQuery.graphql.ts
index e3eb43ba7..7ff554f66 100644
--- a/apps/console2/src/hooks/graph/__generated__/VendorGraphNodeQuery.graphql.ts
+++ b/apps/console2/src/hooks/graph/__generated__/VendorGraphNodeQuery.graphql.ts
@@ -1,5 +1,5 @@
/**
- * @generated SignedSource<<57ff86dca8786cdaf42f34f9cbe379a1>>
+ * @generated SignedSource<<6d50678dccfbe35e4ade26b58cda2ad3>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -18,7 +18,7 @@ export type VendorGraphNodeQuery$data = {
readonly id?: string;
readonly name?: string;
readonly websiteUrl?: string | null | undefined;
- readonly " $fragmentSpreads": FragmentRefs<"useVendorFormFragment">;
+ readonly " $fragmentSpreads": FragmentRefs<"VendorComplianceTabFragment" | "useVendorFormFragment">;
};
};
export type VendorGraphNodeQuery = {
@@ -62,8 +62,22 @@ v4 = {
"name": "websiteUrl",
"storageKey": null
},
-v5 = [
+v5 = {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "__typename",
+ "storageKey": null
+},
+v6 = [
(v2/*: any*/)
+],
+v7 = [
+ {
+ "kind": "Literal",
+ "name": "first",
+ "value": 50
+ }
];
return {
"fragment": {
@@ -90,6 +104,11 @@ return {
"args": null,
"kind": "FragmentSpread",
"name": "useVendorFormFragment"
+ },
+ {
+ "args": null,
+ "kind": "FragmentSpread",
+ "name": "VendorComplianceTabFragment"
}
],
"type": "Vendor",
@@ -116,13 +135,7 @@ return {
"name": "node",
"plural": false,
"selections": [
- {
- "alias": null,
- "args": null,
- "kind": "ScalarField",
- "name": "__typename",
- "storageKey": null
- },
+ (v5/*: any*/),
(v2/*: any*/),
{
"kind": "InlineFragment",
@@ -213,7 +226,7 @@ return {
"kind": "LinkedField",
"name": "businessOwner",
"plural": false,
- "selections": (v5/*: any*/),
+ "selections": (v6/*: any*/),
"storageKey": null
},
{
@@ -223,8 +236,147 @@ return {
"kind": "LinkedField",
"name": "securityOwner",
"plural": false,
- "selections": (v5/*: any*/),
+ "selections": (v6/*: any*/),
"storageKey": null
+ },
+ {
+ "alias": null,
+ "args": (v7/*: any*/),
+ "concreteType": "VendorComplianceReportConnection",
+ "kind": "LinkedField",
+ "name": "complianceReports",
+ "plural": false,
+ "selections": [
+ {
+ "alias": null,
+ "args": null,
+ "concreteType": "VendorComplianceReportEdge",
+ "kind": "LinkedField",
+ "name": "edges",
+ "plural": true,
+ "selections": [
+ {
+ "alias": null,
+ "args": null,
+ "concreteType": "VendorComplianceReport",
+ "kind": "LinkedField",
+ "name": "node",
+ "plural": false,
+ "selections": [
+ (v2/*: any*/),
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "reportDate",
+ "storageKey": null
+ },
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "validUntil",
+ "storageKey": null
+ },
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "reportName",
+ "storageKey": null
+ },
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "fileUrl",
+ "storageKey": null
+ },
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "fileSize",
+ "storageKey": null
+ },
+ (v5/*: any*/)
+ ],
+ "storageKey": null
+ },
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "cursor",
+ "storageKey": null
+ }
+ ],
+ "storageKey": null
+ },
+ {
+ "alias": null,
+ "args": null,
+ "concreteType": "PageInfo",
+ "kind": "LinkedField",
+ "name": "pageInfo",
+ "plural": false,
+ "selections": [
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "endCursor",
+ "storageKey": null
+ },
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "hasNextPage",
+ "storageKey": null
+ },
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "hasPreviousPage",
+ "storageKey": null
+ },
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "startCursor",
+ "storageKey": null
+ }
+ ],
+ "storageKey": null
+ },
+ {
+ "kind": "ClientExtension",
+ "selections": [
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "__id",
+ "storageKey": null
+ }
+ ]
+ }
+ ],
+ "storageKey": "complianceReports(first:50)"
+ },
+ {
+ "alias": null,
+ "args": (v7/*: any*/),
+ "filters": [
+ "orderBy"
+ ],
+ "handle": "connection",
+ "key": "VendorComplianceTabFragment_complianceReports",
+ "kind": "LinkedHandle",
+ "name": "complianceReports"
}
],
"type": "Vendor",
@@ -236,16 +388,16 @@ return {
]
},
"params": {
- "cacheID": "3e7b2628c65e007a1603b982e41fea12",
+ "cacheID": "f4827b073b1e66d0f2fa29283f276ac1",
"id": null,
"metadata": {},
"name": "VendorGraphNodeQuery",
"operationKind": "query",
- "text": "query VendorGraphNodeQuery(\n $vendorId: ID!\n) {\n node(id: $vendorId) {\n __typename\n ... on Vendor {\n id\n name\n websiteUrl\n ...useVendorFormFragment\n }\n id\n }\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": "query VendorGraphNodeQuery(\n $vendorId: ID!\n) {\n node(id: $vendorId) {\n __typename\n ... on Vendor {\n id\n name\n websiteUrl\n ...useVendorFormFragment\n ...VendorComplianceTabFragment\n }\n id\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 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"
}
};
})();
-(node as any).hash = "9a718caa45c75f17f4b910ca2c555781";
+(node as any).hash = "97226bc4f316909815617a30a3a88cd9";
export default node;
diff --git a/apps/console2/src/pages/organizations/SettingsPage.tsx b/apps/console2/src/pages/organizations/SettingsPage.tsx
index 8706ff8e0..1dcda521f 100644
--- a/apps/console2/src/pages/organizations/SettingsPage.tsx
+++ b/apps/console2/src/pages/organizations/SettingsPage.tsx
@@ -5,7 +5,6 @@ import {
Card,
Field,
FileButton,
- IconPlusLarge,
Label,
PageHeader,
Spinner,
@@ -23,7 +22,7 @@ import type {
SettingsPageFragment$key,
} from "./__generated__/SettingsPageFragment.graphql";
import type { ChangeEventHandler } from "react";
-import { groupBy, sprintf } from "@probo/helpers";
+import { sprintf } from "@probo/helpers";
import type { NodeOf } from "/types";
type Props = {
diff --git a/apps/console2/src/pages/organizations/people/PeopleListPage.tsx b/apps/console2/src/pages/organizations/people/PeopleListPage.tsx
index ee94eebeb..b6eed72ce 100644
--- a/apps/console2/src/pages/organizations/people/PeopleListPage.tsx
+++ b/apps/console2/src/pages/organizations/people/PeopleListPage.tsx
@@ -12,23 +12,17 @@ import {
DropdownItem,
IconPencil,
IconTrashCan,
- useConfirm,
} from "@probo/ui";
import { useTranslate } from "@probo/i18n";
import type { PeopleGraphPaginatedQuery } from "/hooks/graph/__generated__/PeopleGraphPaginatedQuery.graphql";
-import { useMutation, type PreloadedQuery } from "react-relay";
-import {
- deletePeopleMutation,
- useDeletePeople,
- usePeopleQuery,
-} from "/hooks/graph/PeopleGraph";
+import { type PreloadedQuery } from "react-relay";
+import { useDeletePeople, usePeopleQuery } from "/hooks/graph/PeopleGraph";
import { SortableTable, SortableTh } from "/components/SortableTable";
import type { PeopleGraphPaginatedFragment$data } from "/hooks/graph/__generated__/PeopleGraphPaginatedFragment.graphql";
import type { NodeOf } from "/types";
import { usePageTitle } from "@probo/hooks";
-import { getRole, sprintf } from "@probo/helpers";
+import { getRole } from "@probo/helpers";
import { CreatePeopleDialog } from "./dialogs/CreatePeopleDialog";
-import type { PeopleGraphDeleteMutation } from "/hooks/graph/__generated__/PeopleGraphDeleteMutation.graphql";
import { useOrganizationId } from "/hooks/useOrganizationId";
import { Link } from "react-router";
diff --git a/apps/console2/src/pages/organizations/policies/dialogs/PolicySignaturesDialog.tsx b/apps/console2/src/pages/organizations/policies/dialogs/PolicySignaturesDialog.tsx
index 19a970f50..3a7537731 100644
--- a/apps/console2/src/pages/organizations/policies/dialogs/PolicySignaturesDialog.tsx
+++ b/apps/console2/src/pages/organizations/policies/dialogs/PolicySignaturesDialog.tsx
@@ -38,6 +38,11 @@ export function PolicySignaturesDialog(props: Props) {
versions[0].id
);
const selectedVersion = versions.find((v) => v.id === selectedVersionId);
+
+ if (!selectedVersion) {
+ return null;
+ }
+
return (
);
}
diff --git a/apps/console2/src/pages/organizations/policies/dialogs/UpdateVersionDialog.tsx b/apps/console2/src/pages/organizations/policies/dialogs/UpdateVersionDialog.tsx
index 530fc5751..c8e6e3198 100644
--- a/apps/console2/src/pages/organizations/policies/dialogs/UpdateVersionDialog.tsx
+++ b/apps/console2/src/pages/organizations/policies/dialogs/UpdateVersionDialog.tsx
@@ -10,7 +10,7 @@ import {
useDialogRef,
useToast,
} from "@probo/ui";
-import { useState, type RefObject } from "react";
+import { type RefObject } from "react";
import { graphql } from "relay-runtime";
import { useMutation } from "react-relay";
import type { PolicyPagePolicyFragment$data } from "../__generated__/PolicyPagePolicyFragment.graphql";
diff --git a/apps/console2/src/pages/organizations/vendors/CreateVendorDialog.tsx b/apps/console2/src/pages/organizations/vendors/CreateVendorDialog.tsx
index cbdfbadda..9971235c0 100644
--- a/apps/console2/src/pages/organizations/vendors/CreateVendorDialog.tsx
+++ b/apps/console2/src/pages/organizations/vendors/CreateVendorDialog.tsx
@@ -13,7 +13,7 @@ import { useVendorSearch } from "/hooks/useVendorSearch";
import { faviconUrl } from "@probo/helpers";
import type { Vendor } from "@probo/vendors";
import { useCreateVendorMutation } from "/hooks/graph/VendorGraph";
-import { type ReactNode, useState } from "react";
+import { type ReactNode } from "react";
type Props = {
children: ReactNode;
diff --git a/apps/console2/src/pages/organizations/vendors/VendorDetailPage.tsx b/apps/console2/src/pages/organizations/vendors/VendorDetailPage.tsx
index 997e8487b..c77685514 100644
--- a/apps/console2/src/pages/organizations/vendors/VendorDetailPage.tsx
+++ b/apps/console2/src/pages/organizations/vendors/VendorDetailPage.tsx
@@ -11,7 +11,6 @@ import {
} from "/hooks/graph/VendorGraph";
import {
ActionDropdown,
- Avatar,
Breadcrumb,
DropdownItem,
IconTrashCan,
diff --git a/apps/console2/src/pages/organizations/vendors/tabs/VendorComplianceTab.tsx b/apps/console2/src/pages/organizations/vendors/tabs/VendorComplianceTab.tsx
index fc24a62af..018a0c539 100644
--- a/apps/console2/src/pages/organizations/vendors/tabs/VendorComplianceTab.tsx
+++ b/apps/console2/src/pages/organizations/vendors/tabs/VendorComplianceTab.tsx
@@ -3,6 +3,24 @@ import { graphql } from "relay-runtime";
import type { VendorComplianceTabFragment$key } from "./__generated__/VendorComplianceTabFragment.graphql";
import { useTranslate } from "@probo/i18n";
import { usePageTitle } from "@probo/hooks";
+import {
+ ActionDropdown,
+ DropdownItem,
+ Dropzone,
+ IconTrashCan,
+ Tbody,
+ Td,
+ Th,
+ Thead,
+ Tr,
+ useConfirm,
+} from "@probo/ui";
+import { useFragment, useMutation, useRefetchableFragment } from "react-relay";
+import type { VendorComplianceTabFragment_report$key } from "./__generated__/VendorComplianceTabFragment_report.graphql";
+import { useMutationWithToasts } from "/hooks/useMutationWithToasts";
+import { sprintf } from "@probo/helpers";
+import { fileSize } from "@probo/helpers/src/file.ts";
+import { SortableTable, SortableTh } from "/components/SortableTable";
const complianceReportsFragment = graphql`
fragment VendorComplianceTabFragment on Vendor
@@ -21,8 +39,10 @@ const complianceReportsFragment = graphql`
before: $before
orderBy: $order
) @connection(key: "VendorComplianceTabFragment_complianceReports") {
+ __id
edges {
node {
+ id
...VendorComplianceTabFragment_report
}
}
@@ -32,6 +52,7 @@ const complianceReportsFragment = graphql`
const complianceReportFragment = graphql`
fragment VendorComplianceTabFragment_report on VendorComplianceReport {
+ id
reportDate
validUntil
reportName
@@ -40,12 +61,161 @@ const complianceReportFragment = graphql`
}
`;
+const uploadReportMutation = graphql`
+ mutation VendorComplianceTabUploadReportMutation(
+ $input: UploadVendorComplianceReportInput!
+ $connections: [ID!]!
+ ) {
+ uploadVendorComplianceReport(input: $input) {
+ vendorComplianceReportEdge @appendEdge(connections: $connections) {
+ node {
+ id
+ ...VendorComplianceTabFragment_report
+ }
+ }
+ }
+ }
+`;
+
+const deleteReportMutation = graphql`
+ mutation VendorComplianceTabDeleteReportMutation(
+ $input: DeleteVendorComplianceReportInput!
+ $connections: [ID!]!
+ ) {
+ deleteVendorComplianceReport(input: $input) {
+ deletedVendorComplianceReportId @deleteEdge(connections: $connections)
+ }
+ }
+`;
+
export default function VendorComplianceTab() {
const { vendor } = useOutletContext<{
- vendor: VendorComplianceTabFragment$key & { name: string };
+ vendor: VendorComplianceTabFragment$key & { name: string; id: string };
}>();
+ const [data, refetch] = useRefetchableFragment(
+ complianceReportsFragment,
+ vendor
+ );
+ const connectionId = data.complianceReports.__id;
+ const reports = data.complianceReports.edges.map((edge) => edge.node);
const { __ } = useTranslate();
usePageTitle(vendor.name + " - " + __("Compliance reports"));
- return
Vendor Compliance
;
+
+ const [mutate, isMutating] = useMutation(uploadReportMutation);
+
+ const handleDrop = (files: File[]) => {
+ const reportDate = new Date().toISOString();
+ for (const file of files) {
+ mutate({
+ variables: {
+ connections: [connectionId],
+ input: {
+ vendorId: vendor.id,
+ reportDate,
+ reportName: file.name,
+ file: null,
+ },
+ },
+ uploadables: {
+ "input.file": file,
+ },
+ });
+ }
+ };
+
+ return (
+
+
+
+
+
+ | {__("Report name")} |
+ {__("Report date")}
+ {__("Valid until")} |
+ {__("File size")} |
+ {__("Actions")} |
+
+
+
+ {reports.map((report) => (
+
+ ))}
+
+
+
+ );
+}
+
+type ReportRowProps = {
+ reportKey: VendorComplianceTabFragment_report$key;
+ connectionId: string;
+};
+
+function ReportRow(props: ReportRowProps) {
+ const { __ } = useTranslate();
+ const report = useFragment(
+ complianceReportFragment,
+ props.reportKey
+ );
+ const { dateFormat } = useTranslate();
+ const confirm = useConfirm();
+ const [deleteReport] = useMutationWithToasts(deleteReportMutation, {
+ successMessage: __("Report deleted successfully"),
+ errorMessage: __("Failed to delete report"),
+ });
+
+ const handleDelete = () => {
+ confirm(
+ () =>
+ deleteReport({
+ variables: {
+ connections: [props.connectionId],
+ input: {
+ reportId: report.id,
+ },
+ },
+ }),
+ {
+ message: sprintf(
+ __(
+ 'This will permanently delete the report "%s". This action cannot be undone.'
+ ),
+ report.reportName
+ ),
+ }
+ );
+ };
+
+ return (
+
+ | {report.reportName} |
+ {dateFormat(report.reportDate)} |
+ {dateFormat(report.validUntil)} |
+ {fileSize(__, report.fileSize)} |
+
+
+
+ {__("Delete")}
+
+
+ |
+
+ );
}
diff --git a/apps/console2/src/pages/organizations/vendors/tabs/__generated__/ComplianceReportListQuery.graphql.ts b/apps/console2/src/pages/organizations/vendors/tabs/__generated__/ComplianceReportListQuery.graphql.ts
index 314e7c1c6..58eba5b49 100644
--- a/apps/console2/src/pages/organizations/vendors/tabs/__generated__/ComplianceReportListQuery.graphql.ts
+++ b/apps/console2/src/pages/organizations/vendors/tabs/__generated__/ComplianceReportListQuery.graphql.ts
@@ -1,5 +1,5 @@
/**
- * @generated SignedSource<<75defbef64a4ee18b514465a44c8eea1>>
+ * @generated SignedSource<<10fdd09e95fc5f2995b91c73cf9fb945>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -211,6 +211,7 @@ return {
"name": "node",
"plural": false,
"selections": [
+ (v12/*: any*/),
{
"alias": null,
"args": null,
@@ -246,7 +247,6 @@ return {
"name": "fileSize",
"storageKey": null
},
- (v12/*: any*/),
(v11/*: any*/)
],
"storageKey": null
@@ -299,6 +299,18 @@ return {
}
],
"storageKey": null
+ },
+ {
+ "kind": "ClientExtension",
+ "selections": [
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "__id",
+ "storageKey": null
+ }
+ ]
}
],
"storageKey": null
@@ -324,16 +336,16 @@ return {
]
},
"params": {
- "cacheID": "76e0b5dbc01424695b8c642e27c0f4ed",
+ "cacheID": "c819e002dfe9f20004fb5bfb4f95b143",
"id": null,
"metadata": {},
"name": "ComplianceReportListQuery",
"operationKind": "query",
- "text": "query ComplianceReportListQuery(\n $after: CursorKey = null\n $before: CursorKey = null\n $first: Int = 50\n $last: Int = null\n $order: VendorComplianceReportOrder = null\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...VendorComplianceTabFragment_16fISc\n id\n }\n}\n\nfragment VendorComplianceTabFragment_16fISc on Vendor {\n complianceReports(first: $first, after: $after, last: $last, before: $before, orderBy: $order) {\n edges {\n node {\n ...VendorComplianceTabFragment_report\n id\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 reportDate\n validUntil\n reportName\n fileUrl\n fileSize\n}\n"
+ "text": "query ComplianceReportListQuery(\n $after: CursorKey = null\n $before: CursorKey = null\n $first: Int = 50\n $last: Int = null\n $order: VendorComplianceReportOrder = null\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...VendorComplianceTabFragment_16fISc\n id\n }\n}\n\nfragment VendorComplianceTabFragment_16fISc on Vendor {\n complianceReports(first: $first, after: $after, last: $last, before: $before, orderBy: $order) {\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"
}
};
})();
-(node as any).hash = "7aae49a4b414ed4db5e7b39f8969c02f";
+(node as any).hash = "1fe4e0b1f9fb781c3b01e539145d610a";
export default node;
diff --git a/apps/console2/src/pages/organizations/vendors/tabs/__generated__/VendorComplianceTabDeleteReportMutation.graphql.ts b/apps/console2/src/pages/organizations/vendors/tabs/__generated__/VendorComplianceTabDeleteReportMutation.graphql.ts
new file mode 100644
index 000000000..20b6bb83a
--- /dev/null
+++ b/apps/console2/src/pages/organizations/vendors/tabs/__generated__/VendorComplianceTabDeleteReportMutation.graphql.ts
@@ -0,0 +1,132 @@
+/**
+ * @generated SignedSource<<6fc7bf1e239dfeccb007ee5a9beefaff>>
+ * @lightSyntaxTransform
+ * @nogrep
+ */
+
+/* tslint:disable */
+/* eslint-disable */
+// @ts-nocheck
+
+import { ConcreteRequest } from 'relay-runtime';
+export type DeleteVendorComplianceReportInput = {
+ reportId: string;
+};
+export type VendorComplianceTabDeleteReportMutation$variables = {
+ connections: ReadonlyArray;
+ input: DeleteVendorComplianceReportInput;
+};
+export type VendorComplianceTabDeleteReportMutation$data = {
+ readonly deleteVendorComplianceReport: {
+ readonly deletedVendorComplianceReportId: string;
+ };
+};
+export type VendorComplianceTabDeleteReportMutation = {
+ response: VendorComplianceTabDeleteReportMutation$data;
+ variables: VendorComplianceTabDeleteReportMutation$variables;
+};
+
+const node: ConcreteRequest = (function(){
+var v0 = {
+ "defaultValue": null,
+ "kind": "LocalArgument",
+ "name": "connections"
+},
+v1 = {
+ "defaultValue": null,
+ "kind": "LocalArgument",
+ "name": "input"
+},
+v2 = [
+ {
+ "kind": "Variable",
+ "name": "input",
+ "variableName": "input"
+ }
+],
+v3 = {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "deletedVendorComplianceReportId",
+ "storageKey": null
+};
+return {
+ "fragment": {
+ "argumentDefinitions": [
+ (v0/*: any*/),
+ (v1/*: any*/)
+ ],
+ "kind": "Fragment",
+ "metadata": null,
+ "name": "VendorComplianceTabDeleteReportMutation",
+ "selections": [
+ {
+ "alias": null,
+ "args": (v2/*: any*/),
+ "concreteType": "DeleteVendorComplianceReportPayload",
+ "kind": "LinkedField",
+ "name": "deleteVendorComplianceReport",
+ "plural": false,
+ "selections": [
+ (v3/*: any*/)
+ ],
+ "storageKey": null
+ }
+ ],
+ "type": "Mutation",
+ "abstractKey": null
+ },
+ "kind": "Request",
+ "operation": {
+ "argumentDefinitions": [
+ (v1/*: any*/),
+ (v0/*: any*/)
+ ],
+ "kind": "Operation",
+ "name": "VendorComplianceTabDeleteReportMutation",
+ "selections": [
+ {
+ "alias": null,
+ "args": (v2/*: any*/),
+ "concreteType": "DeleteVendorComplianceReportPayload",
+ "kind": "LinkedField",
+ "name": "deleteVendorComplianceReport",
+ "plural": false,
+ "selections": [
+ (v3/*: any*/),
+ {
+ "alias": null,
+ "args": null,
+ "filters": null,
+ "handle": "deleteEdge",
+ "key": "",
+ "kind": "ScalarHandle",
+ "name": "deletedVendorComplianceReportId",
+ "handleArgs": [
+ {
+ "kind": "Variable",
+ "name": "connections",
+ "variableName": "connections"
+ }
+ ]
+ }
+ ],
+ "storageKey": null
+ }
+ ]
+ },
+ "params": {
+ "cacheID": "3bfaddbf09d2cd72a0bb72687c1e187e",
+ "id": null,
+ "metadata": {},
+ "name": "VendorComplianceTabDeleteReportMutation",
+ "operationKind": "mutation",
+ "text": "mutation VendorComplianceTabDeleteReportMutation(\n $input: DeleteVendorComplianceReportInput!\n) {\n deleteVendorComplianceReport(input: $input) {\n deletedVendorComplianceReportId\n }\n}\n"
+ }
+};
+})();
+
+(node as any).hash = "7151aea21838384dc10781b64f0209ae";
+
+export default node;
diff --git a/apps/console2/src/pages/organizations/vendors/tabs/__generated__/VendorComplianceTabFragment.graphql.ts b/apps/console2/src/pages/organizations/vendors/tabs/__generated__/VendorComplianceTabFragment.graphql.ts
index 02c81fda7..d09432f83 100644
--- a/apps/console2/src/pages/organizations/vendors/tabs/__generated__/VendorComplianceTabFragment.graphql.ts
+++ b/apps/console2/src/pages/organizations/vendors/tabs/__generated__/VendorComplianceTabFragment.graphql.ts
@@ -1,5 +1,5 @@
/**
- * @generated SignedSource<<079a3066496976bd1238ba194f5a0660>>
+ * @generated SignedSource<>
* @lightSyntaxTransform
* @nogrep
*/
@@ -12,8 +12,10 @@ import { ReaderFragment } from 'relay-runtime';
import { FragmentRefs } from "relay-runtime";
export type VendorComplianceTabFragment$data = {
readonly complianceReports: {
+ readonly __id: string;
readonly edges: ReadonlyArray<{
readonly node: {
+ readonly id: string;
readonly " $fragmentSpreads": FragmentRefs<"VendorComplianceTabFragment_report">;
};
}>;
@@ -31,7 +33,14 @@ import ComplianceReportListQuery_graphql from './ComplianceReportListQuery.graph
const node: ReaderFragment = (function(){
var v0 = [
"complianceReports"
-];
+],
+v1 = {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "id",
+ "storageKey": null
+};
return {
"argumentDefinitions": [
{
@@ -124,6 +133,7 @@ return {
"name": "node",
"plural": false,
"selections": [
+ (v1/*: any*/),
{
"args": null,
"kind": "FragmentSpread",
@@ -187,23 +197,29 @@ return {
}
],
"storageKey": null
+ },
+ {
+ "kind": "ClientExtension",
+ "selections": [
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "__id",
+ "storageKey": null
+ }
+ ]
}
],
"storageKey": null
},
- {
- "alias": null,
- "args": null,
- "kind": "ScalarField",
- "name": "id",
- "storageKey": null
- }
+ (v1/*: any*/)
],
"type": "Vendor",
"abstractKey": null
};
})();
-(node as any).hash = "7aae49a4b414ed4db5e7b39f8969c02f";
+(node as any).hash = "1fe4e0b1f9fb781c3b01e539145d610a";
export default node;
diff --git a/apps/console2/src/pages/organizations/vendors/tabs/__generated__/VendorComplianceTabFragment_report.graphql.ts b/apps/console2/src/pages/organizations/vendors/tabs/__generated__/VendorComplianceTabFragment_report.graphql.ts
index 297116129..ad4487cde 100644
--- a/apps/console2/src/pages/organizations/vendors/tabs/__generated__/VendorComplianceTabFragment_report.graphql.ts
+++ b/apps/console2/src/pages/organizations/vendors/tabs/__generated__/VendorComplianceTabFragment_report.graphql.ts
@@ -1,5 +1,5 @@
/**
- * @generated SignedSource<<01d2d791b86ae99330d4ba2ecb01f1ed>>
+ * @generated SignedSource<<8f56e9dbd8ce8cf0c81d8f2e9f1ad06e>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -13,6 +13,7 @@ import { FragmentRefs } from "relay-runtime";
export type VendorComplianceTabFragment_report$data = {
readonly fileSize: number;
readonly fileUrl: string;
+ readonly id: string;
readonly reportDate: any;
readonly reportName: string;
readonly validUntil: any | null | undefined;
@@ -29,6 +30,13 @@ const node: ReaderFragment = {
"metadata": null,
"name": "VendorComplianceTabFragment_report",
"selections": [
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "id",
+ "storageKey": null
+ },
{
"alias": null,
"args": null,
@@ -69,6 +77,6 @@ const node: ReaderFragment = {
"abstractKey": null
};
-(node as any).hash = "d39b70dfd0a316b158d7dc7253d0ff5e";
+(node as any).hash = "92ad78c9e4b4add9b1f622485d81a6ff";
export default node;
diff --git a/apps/console2/src/pages/organizations/vendors/tabs/__generated__/VendorComplianceTabUploadReportMutation.graphql.ts b/apps/console2/src/pages/organizations/vendors/tabs/__generated__/VendorComplianceTabUploadReportMutation.graphql.ts
new file mode 100644
index 000000000..b23b32ae5
--- /dev/null
+++ b/apps/console2/src/pages/organizations/vendors/tabs/__generated__/VendorComplianceTabUploadReportMutation.graphql.ts
@@ -0,0 +1,226 @@
+/**
+ * @generated SignedSource<<6093b0bc54fa90962376639ec87fa149>>
+ * @lightSyntaxTransform
+ * @nogrep
+ */
+
+/* tslint:disable */
+/* eslint-disable */
+// @ts-nocheck
+
+import { ConcreteRequest } from 'relay-runtime';
+import { FragmentRefs } from "relay-runtime";
+export type UploadVendorComplianceReportInput = {
+ file: any;
+ reportDate: any;
+ reportName: string;
+ validUntil?: any | null | undefined;
+ vendorId: string;
+};
+export type VendorComplianceTabUploadReportMutation$variables = {
+ connections: ReadonlyArray;
+ input: UploadVendorComplianceReportInput;
+};
+export type VendorComplianceTabUploadReportMutation$data = {
+ readonly uploadVendorComplianceReport: {
+ readonly vendorComplianceReportEdge: {
+ readonly node: {
+ readonly id: string;
+ readonly " $fragmentSpreads": FragmentRefs<"VendorComplianceTabFragment_report">;
+ };
+ };
+ };
+};
+export type VendorComplianceTabUploadReportMutation = {
+ response: VendorComplianceTabUploadReportMutation$data;
+ variables: VendorComplianceTabUploadReportMutation$variables;
+};
+
+const node: ConcreteRequest = (function(){
+var v0 = {
+ "defaultValue": null,
+ "kind": "LocalArgument",
+ "name": "connections"
+},
+v1 = {
+ "defaultValue": null,
+ "kind": "LocalArgument",
+ "name": "input"
+},
+v2 = [
+ {
+ "kind": "Variable",
+ "name": "input",
+ "variableName": "input"
+ }
+],
+v3 = {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "id",
+ "storageKey": null
+};
+return {
+ "fragment": {
+ "argumentDefinitions": [
+ (v0/*: any*/),
+ (v1/*: any*/)
+ ],
+ "kind": "Fragment",
+ "metadata": null,
+ "name": "VendorComplianceTabUploadReportMutation",
+ "selections": [
+ {
+ "alias": null,
+ "args": (v2/*: any*/),
+ "concreteType": "UploadVendorComplianceReportPayload",
+ "kind": "LinkedField",
+ "name": "uploadVendorComplianceReport",
+ "plural": false,
+ "selections": [
+ {
+ "alias": null,
+ "args": null,
+ "concreteType": "VendorComplianceReportEdge",
+ "kind": "LinkedField",
+ "name": "vendorComplianceReportEdge",
+ "plural": false,
+ "selections": [
+ {
+ "alias": null,
+ "args": null,
+ "concreteType": "VendorComplianceReport",
+ "kind": "LinkedField",
+ "name": "node",
+ "plural": false,
+ "selections": [
+ (v3/*: any*/),
+ {
+ "args": null,
+ "kind": "FragmentSpread",
+ "name": "VendorComplianceTabFragment_report"
+ }
+ ],
+ "storageKey": null
+ }
+ ],
+ "storageKey": null
+ }
+ ],
+ "storageKey": null
+ }
+ ],
+ "type": "Mutation",
+ "abstractKey": null
+ },
+ "kind": "Request",
+ "operation": {
+ "argumentDefinitions": [
+ (v1/*: any*/),
+ (v0/*: any*/)
+ ],
+ "kind": "Operation",
+ "name": "VendorComplianceTabUploadReportMutation",
+ "selections": [
+ {
+ "alias": null,
+ "args": (v2/*: any*/),
+ "concreteType": "UploadVendorComplianceReportPayload",
+ "kind": "LinkedField",
+ "name": "uploadVendorComplianceReport",
+ "plural": false,
+ "selections": [
+ {
+ "alias": null,
+ "args": null,
+ "concreteType": "VendorComplianceReportEdge",
+ "kind": "LinkedField",
+ "name": "vendorComplianceReportEdge",
+ "plural": false,
+ "selections": [
+ {
+ "alias": null,
+ "args": null,
+ "concreteType": "VendorComplianceReport",
+ "kind": "LinkedField",
+ "name": "node",
+ "plural": false,
+ "selections": [
+ (v3/*: any*/),
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "reportDate",
+ "storageKey": null
+ },
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "validUntil",
+ "storageKey": null
+ },
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "reportName",
+ "storageKey": null
+ },
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "fileUrl",
+ "storageKey": null
+ },
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "fileSize",
+ "storageKey": null
+ }
+ ],
+ "storageKey": null
+ }
+ ],
+ "storageKey": null
+ },
+ {
+ "alias": null,
+ "args": null,
+ "filters": null,
+ "handle": "appendEdge",
+ "key": "",
+ "kind": "LinkedHandle",
+ "name": "vendorComplianceReportEdge",
+ "handleArgs": [
+ {
+ "kind": "Variable",
+ "name": "connections",
+ "variableName": "connections"
+ }
+ ]
+ }
+ ],
+ "storageKey": null
+ }
+ ]
+ },
+ "params": {
+ "cacheID": "fb9be687deba8567502e207b0969b2b5",
+ "id": null,
+ "metadata": {},
+ "name": "VendorComplianceTabUploadReportMutation",
+ "operationKind": "mutation",
+ "text": "mutation VendorComplianceTabUploadReportMutation(\n $input: UploadVendorComplianceReportInput!\n) {\n uploadVendorComplianceReport(input: $input) {\n vendorComplianceReportEdge {\n node {\n id\n ...VendorComplianceTabFragment_report\n }\n }\n }\n}\n\nfragment VendorComplianceTabFragment_report on VendorComplianceReport {\n id\n reportDate\n validUntil\n reportName\n fileUrl\n fileSize\n}\n"
+ }
+};
+})();
+
+(node as any).hash = "4ed863a250b0686340056f4f77eb8ab8";
+
+export default node;
diff --git a/packages/helpers/src/file.test.ts b/packages/helpers/src/file.test.ts
new file mode 100644
index 000000000..8752fa857
--- /dev/null
+++ b/packages/helpers/src/file.test.ts
@@ -0,0 +1,13 @@
+import { describe, it, expect } from "vitest";
+import { isEmpty } from "./array";
+import { fileSize } from "./file";
+
+describe("file", () => {
+ it("should display a file size in a human readable format", () => {
+ const fakeTranslator = (s: string) => s;
+ expect(fileSize(fakeTranslator, 4911)).toMatchInlineSnapshot(
+ `"4.8 KB"`,
+ );
+ expect(fileSize(fakeTranslator, 20)).toMatchInlineSnapshot(`"20 B"`);
+ });
+});
diff --git a/packages/helpers/src/file.ts b/packages/helpers/src/file.ts
new file mode 100644
index 000000000..9fefa547c
--- /dev/null
+++ b/packages/helpers/src/file.ts
@@ -0,0 +1,19 @@
+/**
+ * Return the file size in a human readable format
+ */
+export function fileSize(__: (s: string) => string, size: number): string {
+ if (size < 0) return "";
+ if (size === 0) return `0 ${__("B")}`;
+
+ const units = [__("B"), __("KB"), __("MB"), __("GB"), __("TB")];
+ const i = Math.floor(Math.log(size) / Math.log(1024));
+
+ // Don't go beyond available units
+ const unitIndex = Math.min(i, units.length - 1);
+
+ // Convert to the appropriate unit with 2 decimal places
+ const convertedSize = size / Math.pow(1024, unitIndex);
+ const formattedSize = Math.round(convertedSize * 100) / 100;
+
+ return `${formattedSize} ${units[unitIndex]}`;
+}
diff --git a/packages/hooks/src/index.ts b/packages/hooks/src/index.ts
index ee2bbe2c1..74bacdd55 100644
--- a/packages/hooks/src/index.ts
+++ b/packages/hooks/src/index.ts
@@ -1,2 +1,3 @@
export { usePageTitle } from "./usePageTitle";
export { useToggle } from "./useToggle";
+export { useRefSync } from "./useRefSync";
diff --git a/packages/hooks/src/useRefSync.ts b/packages/hooks/src/useRefSync.ts
new file mode 100644
index 000000000..728807718
--- /dev/null
+++ b/packages/hooks/src/useRefSync.ts
@@ -0,0 +1,7 @@
+import { useRef } from "react";
+
+export function useRefSync(value: T) {
+ const ref = useRef(value);
+ ref.current = value;
+ return ref;
+}
diff --git a/packages/ui/src/Atoms/Dropdown/Dropdown.tsx b/packages/ui/src/Atoms/Dropdown/Dropdown.tsx
index 03624961a..db67d07a7 100644
--- a/packages/ui/src/Atoms/Dropdown/Dropdown.tsx
+++ b/packages/ui/src/Atoms/Dropdown/Dropdown.tsx
@@ -13,7 +13,7 @@ type Props = PropsWithChildren<{
}>;
export const dropdown = tv({
- base: "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-dropdown-menu-content-transform-origin] z-50 p-2 shadow-mid min-w-[8rem] bg-level-1 overflow-y-auto overflow-x-hidden rounded-2xl border-border-low",
+ base: "z-50 p-2 shadow-mid min-w-[8rem] bg-level-1 overflow-y-auto overflow-x-hidden rounded-2xl border-border-low",
});
export function Dropdown({ children, toggle, className, open }: Props) {
diff --git a/packages/ui/src/Atoms/Dropzone/Dropzone.stories.tsx b/packages/ui/src/Atoms/Dropzone/Dropzone.stories.tsx
index daf4a4100..dd88e22b0 100644
--- a/packages/ui/src/Atoms/Dropzone/Dropzone.stories.tsx
+++ b/packages/ui/src/Atoms/Dropzone/Dropzone.stories.tsx
@@ -9,4 +9,24 @@ export default {
type Story = StoryObj;
-export const Default: Story = {};
+export const Default: Story = {
+ args: {
+ description: "Maximum 500 MB file size",
+ isUploading: false,
+ disabled: false,
+ },
+};
+
+export const Disabled: Story = {
+ args: {
+ ...Default.args,
+ disabled: true,
+ },
+};
+
+export const Uploading: Story = {
+ args: {
+ ...Default.args,
+ isUploading: true,
+ },
+};
diff --git a/packages/ui/src/Atoms/Dropzone/Dropzone.tsx b/packages/ui/src/Atoms/Dropzone/Dropzone.tsx
index 70bccd76b..7ed72d04e 100644
--- a/packages/ui/src/Atoms/Dropzone/Dropzone.tsx
+++ b/packages/ui/src/Atoms/Dropzone/Dropzone.tsx
@@ -1,24 +1,129 @@
+import { useRefSync } from "@probo/hooks";
+import { useTranslate } from "@probo/i18n";
import { useCallback } from "react";
-import { useDropzone } from "react-dropzone";
+import { useDropzone, type FileRejection } from "react-dropzone";
+import { tv } from "tailwind-variants";
+import { IconPageCross, IconUpload } from "../Icons";
+import { Spinner } from "../Spinner/Spinner";
-type Props = {};
+type Props = {
+ description: string;
+ isUploading: boolean;
+ disabled?: boolean;
+ accept?: Record;
+ maxSize?: number; // maxSize in MB
+ onDrop: (acceptedFiles: File[]) => void;
+};
-export function Dropzone({}: Props) {
- const onDrop = useCallback((acceptedFiles) => {
- // Do something with the files
- }, []);
- const { getRootProps, getInputProps, isDragActive } = useDropzone({
- onDrop,
+export const dropzone = tv({
+ slots: {
+ wrapper:
+ "bg-subtle border border border-border-low p-2 rounded-[20px] outline-none focus-visible:shadow-focus",
+ zone: "bg-secondary min-h-46 border border-border-low border-dashed rounded-2xl flex items-center justify-center",
+ title: "flex gap-2 text-sm font-medium text-center justify-center",
+ description: "text-xs text-txt-tertiary text-center",
+ },
+ variants: {
+ isDragActive: {
+ true: {
+ wrapper: "border-border-success shadow-focus",
+ zone: "border-border-success",
+ },
+ },
+ hasError: {
+ true: {
+ wrapper: "bg-danger",
+ zone: "bg-invert",
+ title: "text-txt-danger",
+ description: "text-txt-danger",
+ },
+ },
+ disabled: {
+ true: {
+ wrapper: "opacity-60 cursor-default",
+ zone: "bg-highlight",
+ },
+ false: {
+ zone: "hover:bg-secondary-hover",
+ },
+ },
+ },
+ defaultVariants: {
+ isDragActive: false,
+ disabled: false,
+ hasError: false,
+ },
+});
+
+const MB = 1024;
+
+export function Dropzone(props: Props) {
+ const { __ } = useTranslate();
+ const onDropRef = useRefSync(props.onDrop);
+ const onDrop = useCallback(
+ (files: File[]) => {
+ onDropRef.current(files);
+ },
+ [onDropRef],
+ );
+ const { getRootProps, getInputProps, isDragActive, fileRejections } =
+ useDropzone({
+ disabled: props.disabled || props.isUploading,
+ accept: props.accept,
+ onDrop,
+ maxSize: props.maxSize ? props.maxSize * MB : undefined,
+ });
+ const error = getDropzoneError(__, fileRejections);
+ const { wrapper, zone, title, description } = dropzone({
+ ...props,
+ isDragActive,
+ hasError: !!error,
});
+ console.log(error);
+
return (
-
-
- {isDragActive ? (
-
Drop the files here ...
- ) : (
-
Drag 'n' drop some files here, or click to select files
- )}
+
+
+
+ {props.isUploading ? (
+
+
+ {__("Uploading file")}
+
+ ) : (
+
+
+ {error ? (
+
+ ) : (
+
+ )}
+ {error ?? __("Drag and drop or browse files")}
+
+
{props.description}
+
+ )}
+
);
}
+
+function getDropzoneError(
+ __: (s: string) => string,
+ fileRejections: readonly FileRejection[],
+) {
+ if (fileRejections.length === 0) {
+ return null;
+ }
+
+ const code = fileRejections[0].errors[0].code;
+ switch (code) {
+ case "file-invalid-type":
+ return __("This file is not supported");
+ case "file-too-large":
+ return __("This file is too large");
+ default:
+ return __("Something went wrong");
+ }
+}
diff --git a/packages/ui/src/Molecules/Dialog/ConfirmDialog.tsx b/packages/ui/src/Molecules/Dialog/ConfirmDialog.tsx
index b5c7c1a57..2cb95c3d4 100644
--- a/packages/ui/src/Molecules/Dialog/ConfirmDialog.tsx
+++ b/packages/ui/src/Molecules/Dialog/ConfirmDialog.tsx
@@ -1,21 +1,13 @@
import { useTranslate } from "@probo/i18n";
import {
Root,
- Trigger,
Overlay,
Content,
Title,
Description,
Cancel,
} from "@radix-ui/react-alert-dialog";
-import {
- useCallback,
- useRef,
- useState,
- type ComponentProps,
- type ReactNode,
- type RefObject,
-} from "react";
+import { useCallback, useState, type ComponentProps } from "react";
import { Button } from "../../Atoms/Button/Button";
import { Root as Portal } from "@radix-ui/react-portal";
import { dialog } from "./Dialog";
diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts
index 36f95aabb..381f4cd61 100644
--- a/packages/ui/src/index.ts
+++ b/packages/ui/src/index.ts
@@ -32,6 +32,7 @@ export { PropertyRow } from "./Atoms/PropertyRow/PropertyRow";
export { Table, Thead, Tr, Tbody, Td, Th } from "./Atoms/Table/Table";
export { Tabs, TabLink } from "./Atoms/Tabs/Tabs";
export { Markdown } from "./Atoms/Markdown/Markdown";
+export { Dropzone } from "./Atoms/Dropzone/Dropzone";
// Molecules
export {