From 780693dd0903494f50e675c46047c7462ebe55a7 Mon Sep 17 00:00:00 2001 From: Bryan Frimin Date: Tue, 22 Apr 2025 08:30:57 -0700 Subject: [PATCH] Redesign vendor list page Signed-off-by: Bryan Frimin --- CHANGELOG.md | 3 +- .../organizations/vendors/ListVendorView.tsx | 351 +++++++++++++----- ...tVendorViewCreateVendorMutation.graphql.ts | 16 +- .../ListVendorViewPaginationQuery.graphql.ts | 15 +- .../ListVendorViewQuery.graphql.ts | 13 +- .../ListVendorView_vendors.graphql.ts | 12 +- 6 files changed, 304 insertions(+), 106 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe350380a..698011bc0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,8 @@ All notable changes to this project will be documented in this file. ### Changed -- Completely redesigned vendor detail page with a cleaner, more intuitive layout +- Completely redesigned vendor list page +- Completely redesigned vendor detail page - Improved compliance reports table with better file size formatting and date display - People may be linked to user diff --git a/apps/console/src/pages/organizations/vendors/ListVendorView.tsx b/apps/console/src/pages/organizations/vendors/ListVendorView.tsx index 045418f3e..aa2b0bae4 100644 --- a/apps/console/src/pages/organizations/vendors/ListVendorView.tsx +++ b/apps/console/src/pages/organizations/vendors/ListVendorView.tsx @@ -8,9 +8,9 @@ import { usePaginationFragment, } from "react-relay"; import { useSearchParams, useParams } from "react-router"; -import { Avatar, AvatarFallback } from "@/components/ui/avatar"; +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Badge } from "@/components/ui/badge"; -import { Store, ChevronRight, Trash2 } from "lucide-react"; +import { Store, Plus, MoreHorizontal } from "lucide-react"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { Link } from "react-router"; @@ -83,6 +83,7 @@ const vendorListFragment = graphql` id name description + websiteUrl createdAt updatedAt } @@ -108,6 +109,7 @@ const createVendorMutation = graphql` id name description + websiteUrl createdAt updatedAt } @@ -141,7 +143,7 @@ function LoadAboveButton({ } return ( -
+
+ } > -
-
+ {showAddVendorDropdown && ( +

Add a vendor

@@ -268,18 +334,7 @@ function ListVendorContent({ placeholder="Type vendor's name" value={searchTerm} style={{ borderRadius: "0.3rem" }} - onChange={(e) => { - const value = e.target.value; - setSearchTerm(value); - if (value.trim() === "") { - setFilteredVendors([]); - } else { - const results = fuse - .search(value) - .map((result) => result.item); - setFilteredVendors(results); - } - }} + onChange={handleSearchChange} disabled={isLoadingVendors} /> {isLoadingVendors && ( @@ -291,12 +346,12 @@ function ListVendorContent({ {searchTerm.trim() !== "" && (
{filteredVendors.map((vendor) => ( ))}
)}
+ )} -
- {vendors.map((vendor) => ( - -
-
- - {vendor?.name?.[0]} - -
-

{vendor?.name}

-
-
+
+ + + + + + + + + + + + {vendors.map((vendor) => { + const riskStatus = getRiskStatus(vendor.id); + return ( + + + + + + + + ); + })} + +
- - {vendor.riskTier} - - - + +
- - - ))} - +
+
+ Last update + + + +
+
+
+ Risk + + + +
+
+
+ Compliance status + + + +
+
+
+ + {(() => { + const faviconUrl = vendor.websiteUrl ? getFaviconUrl(vendor.websiteUrl) : null; + return faviconUrl ? ( + + ) : ( + {vendor.name?.[0]} + ); + })()} + + + {vendor.name} + +
+
+ {formatDate(vendor.updatedAt)} + + + {riskStatus === "CRITICAL" + ? "Critical" + : riskStatus === "SIGNIFICANT" + ? "Medium" + : "General"} + + + + {riskStatus === "CRITICAL" + ? "Late" + : riskStatus === "SIGNIFICANT" + ? "In Progress" + : "Completed"} + + + +
> + * @generated SignedSource<<55ac3df9e65daff882c0d44f7b4904c2>> * @lightSyntaxTransform * @nogrep */ @@ -43,6 +43,7 @@ export type ListVendorViewCreateVendorMutation$data = { readonly id: string; readonly name: string; readonly updatedAt: string; + readonly websiteUrl: string | null | undefined; }; }; }; @@ -107,6 +108,13 @@ v3 = { "name": "description", "storageKey": null }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "websiteUrl", + "storageKey": null + }, { "alias": null, "args": null, @@ -193,16 +201,16 @@ return { ] }, "params": { - "cacheID": "03c1d61a3a252ca74bd8da1821f1e0b9", + "cacheID": "4ff1631133af0ca4a0d3109dbd1bda4f", "id": null, "metadata": {}, "name": "ListVendorViewCreateVendorMutation", "operationKind": "mutation", - "text": "mutation ListVendorViewCreateVendorMutation(\n $input: CreateVendorInput!\n) {\n createVendor(input: $input) {\n vendorEdge {\n node {\n id\n name\n description\n createdAt\n updatedAt\n }\n }\n }\n}\n" + "text": "mutation ListVendorViewCreateVendorMutation(\n $input: CreateVendorInput!\n) {\n createVendor(input: $input) {\n vendorEdge {\n node {\n id\n name\n description\n websiteUrl\n createdAt\n updatedAt\n }\n }\n }\n}\n" } }; })(); -(node as any).hash = "05ca888317537533f7345000f0948c0c"; +(node as any).hash = "94be3494153c93d4bf4d1a6324b95f17"; export default node; diff --git a/apps/console/src/pages/organizations/vendors/__generated__/ListVendorViewPaginationQuery.graphql.ts b/apps/console/src/pages/organizations/vendors/__generated__/ListVendorViewPaginationQuery.graphql.ts index 7ab7b609d..c8a86ce33 100644 --- a/apps/console/src/pages/organizations/vendors/__generated__/ListVendorViewPaginationQuery.graphql.ts +++ b/apps/console/src/pages/organizations/vendors/__generated__/ListVendorViewPaginationQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<> + * @generated SignedSource<<2d5b613cbbbd051c4c1f1561f55bf644>> * @lightSyntaxTransform * @nogrep */ @@ -210,6 +210,13 @@ return { "name": "description", "storageKey": null }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "websiteUrl", + "storageKey": null + }, { "alias": null, "args": null, @@ -313,16 +320,16 @@ return { ] }, "params": { - "cacheID": "b4165eeba6363c964b11f14f678d5338", + "cacheID": "b1055c67d0940ecd9189ec80410a8add", "id": null, "metadata": {}, "name": "ListVendorViewPaginationQuery", "operationKind": "query", - "text": "query ListVendorViewPaginationQuery(\n $after: CursorKey\n $before: CursorKey\n $first: Int\n $last: Int\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...ListVendorView_vendors_pbnwq\n id\n }\n}\n\nfragment ListVendorView_vendors_pbnwq on Organization {\n vendors(first: $first, after: $after, last: $last, before: $before, orderBy: {direction: ASC, field: NAME}) {\n edges {\n node {\n id\n name\n description\n createdAt\n updatedAt\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n id\n}\n" + "text": "query ListVendorViewPaginationQuery(\n $after: CursorKey\n $before: CursorKey\n $first: Int\n $last: Int\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...ListVendorView_vendors_pbnwq\n id\n }\n}\n\nfragment ListVendorView_vendors_pbnwq on Organization {\n vendors(first: $first, after: $after, last: $last, before: $before, orderBy: {direction: ASC, field: NAME}) {\n edges {\n node {\n id\n name\n description\n websiteUrl\n createdAt\n updatedAt\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n id\n}\n" } }; })(); -(node as any).hash = "477e430705f0f3cbadb8dfac89e24629"; +(node as any).hash = "3908e5fd1af7e0cb367a480fc9fc437e"; export default node; diff --git a/apps/console/src/pages/organizations/vendors/__generated__/ListVendorViewQuery.graphql.ts b/apps/console/src/pages/organizations/vendors/__generated__/ListVendorViewQuery.graphql.ts index 34ec030a7..a26d463b8 100644 --- a/apps/console/src/pages/organizations/vendors/__generated__/ListVendorViewQuery.graphql.ts +++ b/apps/console/src/pages/organizations/vendors/__generated__/ListVendorViewQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<> + * @generated SignedSource<<4d1328e7df631fabe3c8b7dccdf5ffb0>> * @lightSyntaxTransform * @nogrep */ @@ -212,6 +212,13 @@ return { "name": "description", "storageKey": null }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "websiteUrl", + "storageKey": null + }, { "alias": null, "args": null, @@ -315,12 +322,12 @@ return { ] }, "params": { - "cacheID": "d93a40f389769d5895b502cb8e7a2c16", + "cacheID": "f4269cea33bcb3665cfc49bc350cf426", "id": null, "metadata": {}, "name": "ListVendorViewQuery", "operationKind": "query", - "text": "query ListVendorViewQuery(\n $organizationId: ID!\n $first: Int\n $after: CursorKey\n $last: Int\n $before: CursorKey\n) {\n organization: node(id: $organizationId) {\n __typename\n id\n ...ListVendorView_vendors_pbnwq\n }\n}\n\nfragment ListVendorView_vendors_pbnwq on Organization {\n vendors(first: $first, after: $after, last: $last, before: $before, orderBy: {direction: ASC, field: NAME}) {\n edges {\n node {\n id\n name\n description\n createdAt\n updatedAt\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n id\n}\n" + "text": "query ListVendorViewQuery(\n $organizationId: ID!\n $first: Int\n $after: CursorKey\n $last: Int\n $before: CursorKey\n) {\n organization: node(id: $organizationId) {\n __typename\n id\n ...ListVendorView_vendors_pbnwq\n }\n}\n\nfragment ListVendorView_vendors_pbnwq on Organization {\n vendors(first: $first, after: $after, last: $last, before: $before, orderBy: {direction: ASC, field: NAME}) {\n edges {\n node {\n id\n name\n description\n websiteUrl\n createdAt\n updatedAt\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n id\n}\n" } }; })(); diff --git a/apps/console/src/pages/organizations/vendors/__generated__/ListVendorView_vendors.graphql.ts b/apps/console/src/pages/organizations/vendors/__generated__/ListVendorView_vendors.graphql.ts index 5716807d3..8ec091522 100644 --- a/apps/console/src/pages/organizations/vendors/__generated__/ListVendorView_vendors.graphql.ts +++ b/apps/console/src/pages/organizations/vendors/__generated__/ListVendorView_vendors.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<1ea33fd638cd871d891e3cd41e1f3372>> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -21,6 +21,7 @@ export type ListVendorView_vendors$data = { readonly id: string; readonly name: string; readonly updatedAt: string; + readonly websiteUrl: string | null | undefined; }; }>; readonly pageInfo: { @@ -153,6 +154,13 @@ return { "name": "description", "storageKey": null }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "websiteUrl", + "storageKey": null + }, { "alias": null, "args": null, @@ -248,6 +256,6 @@ return { }; })(); -(node as any).hash = "477e430705f0f3cbadb8dfac89e24629"; +(node as any).hash = "3908e5fd1af7e0cb367a480fc9fc437e"; export default node;