Update vendor list page style
Signed-off-by: gearnode <bryan@frimin.fr>
This commit is contained in:
@@ -10,7 +10,7 @@ import {
|
|||||||
import { useSearchParams } from "react-router";
|
import { useSearchParams } from "react-router";
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { CircleUser, Globe, Shield, Store } from "lucide-react";
|
import { CircleUser, Globe, Shield, Store, ChevronRight, Trash2 } from "lucide-react";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Link } from "react-router";
|
import { Link } from "react-router";
|
||||||
@@ -52,6 +52,7 @@ const vendorListFragment = graphql`
|
|||||||
node {
|
node {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
|
description
|
||||||
createdAt
|
createdAt
|
||||||
updatedAt
|
updatedAt
|
||||||
}
|
}
|
||||||
@@ -207,93 +208,139 @@ function VendorListContent({
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-6 space-y-6">
|
<div className="space-y-6">
|
||||||
<div className="space-y-1">
|
<div>
|
||||||
<h2 className="text-2xl font-semibold tracking-tight">Vendors</h2>
|
<h2 className="text-2xl font-semibold mb-1">Vendors</h2>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-muted-foreground">
|
||||||
Keep track of your company's vendors and their compliance status.
|
Vendors are third-party services that your company uses. Add them to keep track of their risk and compliance status.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="rounded-lg border p-4 shadow-sm bg-card text-card-foreground">
|
<div className="rounded-xl border bg-card p-4">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center gap-2 mb-4">
|
||||||
<div className="flex items-center gap-4">
|
<Store className="h-5 w-5" />
|
||||||
<Avatar className="h-10 w-10">
|
<h3 className="font-medium">Add a vendor</h3>
|
||||||
<AvatarFallback>+</AvatarFallback>
|
|
||||||
</Avatar>
|
|
||||||
<div className="space-y-1">
|
|
||||||
<p className="text-sm font-medium leading-none">Add a vendor</p>
|
|
||||||
<p className="text-sm text-muted-foreground">
|
|
||||||
Search and add new vendors
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-4">
|
|
||||||
<div className="flex flex-col gap-3 relative">
|
|
||||||
<Input
|
|
||||||
type="text"
|
|
||||||
placeholder="Type vendor's name"
|
|
||||||
className="w-64"
|
|
||||||
value={searchTerm}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{searchTerm.trim() !== "" && filteredVendors.length > 0 && (
|
|
||||||
<div className="absolute top-full mt-1 w-64 max-h-48 overflow-y-auto rounded-md border bg-white shadow-lg z-10">
|
|
||||||
{filteredVendors.map((vendor) => (
|
|
||||||
<div
|
|
||||||
key={vendor?.id}
|
|
||||||
className="px-3 py-2 hover:bg-gray-100 cursor-pointer"
|
|
||||||
onClick={() => {
|
|
||||||
createVendor({
|
|
||||||
variables: {
|
|
||||||
connections: [vendorsConnection.vendors.__id],
|
|
||||||
input: {
|
|
||||||
organizationId: data.currentOrganization.id,
|
|
||||||
name: vendor.name,
|
|
||||||
description: "",
|
|
||||||
serviceStartAt: new Date().toISOString(),
|
|
||||||
serviceCriticality: "LOW",
|
|
||||||
riskTier: "GENERAL",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
onCompleted(response) {
|
|
||||||
setSearchTerm("");
|
|
||||||
setFilteredVendors([]);
|
|
||||||
toast({
|
|
||||||
title: "Vendor added",
|
|
||||||
description:
|
|
||||||
"The vendor has been added successfully",
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Store className="h-4 w-4" />
|
|
||||||
<span>{vendor.name}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<Button variant="secondary">
|
|
||||||
Search
|
|
||||||
<span className="ml-2">→</span>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex gap-2 relative">
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{searchTerm.trim() !== "" && filteredVendors.length > 0 && (
|
||||||
|
<div style={{ borderRadius: "0.3rem" }} className="absolute top-full left-0 mt-1 w-[calc(100%-100px)] max-h-48 overflow-y-auto border bg-popover shadow-md z-10">
|
||||||
|
{filteredVendors.map((vendor) => (
|
||||||
|
<button
|
||||||
|
key={vendor.id}
|
||||||
|
className="w-full px-3 py-2 text-left hover:bg-accent"
|
||||||
|
onClick={() => {
|
||||||
|
createVendor({
|
||||||
|
variables: {
|
||||||
|
connections: [vendorsConnection.vendors.__id],
|
||||||
|
input: {
|
||||||
|
organizationId: data.currentOrganization.id,
|
||||||
|
name: vendor.name,
|
||||||
|
description: "",
|
||||||
|
serviceStartAt: new Date().toISOString(),
|
||||||
|
serviceCriticality: "LOW",
|
||||||
|
riskTier: "GENERAL",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
onCompleted() {
|
||||||
|
setSearchTerm("");
|
||||||
|
setFilteredVendors([]);
|
||||||
|
toast({
|
||||||
|
title: "Vendor added",
|
||||||
|
description: "The vendor has been added successfully",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{vendor.name}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
{vendors.map((vendor) => (
|
||||||
|
<Link
|
||||||
|
key={vendor?.id}
|
||||||
|
to={`/vendors/${vendor?.id}`}
|
||||||
|
className="block"
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-between p-4 rounded-xl border bg-card hover:bg-accent/5 transition-colors">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<Avatar className="h-8 w-8">
|
||||||
|
<AvatarFallback>{vendor?.name?.[0]}</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<p className="font-medium">{vendor?.name}</p>
|
||||||
|
{vendor?.description && (
|
||||||
|
<>
|
||||||
|
<span className="text-muted-foreground">•</span>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
{vendor.description}
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Badge
|
||||||
|
variant="secondary"
|
||||||
|
className="bg-[#8BB563] text-white rounded-full px-3 py-0.5 text-xs font-medium"
|
||||||
|
>
|
||||||
|
Low Risk
|
||||||
|
</Badge>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
className="h-8 w-8 text-muted-foreground hover:bg-transparent [&>svg]:hover:text-destructive"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault(); // Prevent navigation
|
||||||
|
if (window.confirm("Are you sure you want to delete this vendor?")) {
|
||||||
|
deleteVendor({
|
||||||
|
variables: {
|
||||||
|
connections: [vendorsConnection.vendors.__id],
|
||||||
|
input: {
|
||||||
|
vendorId: vendor.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
onCompleted() {
|
||||||
|
toast({
|
||||||
|
title: "Vendor deleted",
|
||||||
|
description: "The vendor has been deleted successfully",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Trash2 className="h-4 w-4 transition-colors" />
|
||||||
|
</Button>
|
||||||
|
<ChevronRight className="h-4 w-4 text-muted-foreground" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<LoadAboveButton
|
<LoadAboveButton
|
||||||
@@ -310,82 +357,6 @@ function VendorListContent({
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
{vendors.map((vendor) => (
|
|
||||||
<div
|
|
||||||
key={vendor?.id}
|
|
||||||
className="block hover:bg-accent/50 transition-colors"
|
|
||||||
>
|
|
||||||
<div className="flex items-center justify-between p-4 rounded-lg border bg-card text-card-foreground shadow-sm">
|
|
||||||
<Link
|
|
||||||
to={`/vendors/${vendor?.id}`}
|
|
||||||
className="flex items-center gap-4 flex-1"
|
|
||||||
>
|
|
||||||
<div className="flex items-center gap-4">
|
|
||||||
<Avatar className="h-10 w-10">
|
|
||||||
<AvatarFallback>{vendor?.name?.[0]}</AvatarFallback>
|
|
||||||
</Avatar>
|
|
||||||
<div className="space-y-1">
|
|
||||||
<p className="text-sm font-medium leading-none">
|
|
||||||
{vendor?.name}
|
|
||||||
</p>
|
|
||||||
<p className="text-sm text-muted-foreground">
|
|
||||||
Vendor since{" "}
|
|
||||||
{new Date(vendor?.createdAt).toLocaleDateString()}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
<div className="flex items-center gap-4">
|
|
||||||
<Badge variant="secondary" className="font-medium">
|
|
||||||
Active
|
|
||||||
</Badge>
|
|
||||||
<div className="flex gap-1">
|
|
||||||
<CircleUser className="h-4 w-4 text-muted-foreground" />
|
|
||||||
<Globe className="h-4 w-4 text-muted-foreground" />
|
|
||||||
<Shield className="h-4 w-4 text-muted-foreground" />
|
|
||||||
</div>
|
|
||||||
<Badge variant="outline" className="text-muted-foreground">
|
|
||||||
Not assessed
|
|
||||||
</Badge>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="sm"
|
|
||||||
className="text-destructive hover:text-destructive hover:bg-destructive/10"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
if (
|
|
||||||
window.confirm(
|
|
||||||
"Are you sure you want to delete this vendor?",
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
deleteVendor({
|
|
||||||
variables: {
|
|
||||||
connections: [vendorsConnection.vendors.__id],
|
|
||||||
input: {
|
|
||||||
vendorId: vendor.id,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
onCompleted() {
|
|
||||||
toast({
|
|
||||||
title: "Vendor deleted",
|
|
||||||
description:
|
|
||||||
"The vendor has been deleted successfully",
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Delete
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<LoadBelowButton
|
<LoadBelowButton
|
||||||
isLoading={isLoadingNext}
|
isLoading={isLoadingNext}
|
||||||
hasMore={hasNext}
|
hasMore={hasNext}
|
||||||
@@ -406,14 +377,21 @@ function VendorListContent({
|
|||||||
|
|
||||||
function VendorListFallback() {
|
function VendorListFallback() {
|
||||||
return (
|
return (
|
||||||
<div className="p-6 space-y-6">
|
<div className="space-y-6">
|
||||||
<div className="space-y-1">
|
<div>
|
||||||
<div className="h-8 w-48 bg-muted animate-pulse rounded" />
|
<div className="h-8 w-48 bg-muted animate-pulse rounded" />
|
||||||
<div className="h-4 w-96 bg-muted animate-pulse rounded" />
|
<div className="h-4 w-96 bg-muted animate-pulse rounded mt-1" />
|
||||||
|
</div>
|
||||||
|
<div className="rounded-xl border bg-card p-4 space-y-4">
|
||||||
|
<div className="h-5 w-32 bg-muted animate-pulse rounded" />
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<div className="h-10 flex-1 bg-muted animate-pulse rounded" />
|
||||||
|
<div className="h-10 w-32 bg-muted animate-pulse rounded" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{[1, 2, 3].map((i) => (
|
{[1, 2, 3].map((i) => (
|
||||||
<div key={i} className="h-20 bg-muted animate-pulse rounded-lg" />
|
<div key={i} className="h-[72px] bg-muted animate-pulse rounded-xl" />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* @generated SignedSource<<86f3151a87bc97ed9d37df57377f1cce>>
|
* @generated SignedSource<<035db05f931b88ee09b1bbdd2c39a7e7>>
|
||||||
* @lightSyntaxTransform
|
* @lightSyntaxTransform
|
||||||
* @nogrep
|
* @nogrep
|
||||||
*/
|
*/
|
||||||
@@ -186,6 +186,13 @@ return {
|
|||||||
"name": "name",
|
"name": "name",
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "description",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"alias": null,
|
"alias": null,
|
||||||
"args": null,
|
"args": null,
|
||||||
@@ -287,16 +294,16 @@ return {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"params": {
|
"params": {
|
||||||
"cacheID": "3ce437d0d8e4415db28f78cf8c9aac7c",
|
"cacheID": "61e9346ec50a3f4b1397fa2e6165ab3e",
|
||||||
"id": null,
|
"id": null,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"name": "VendorListPagePaginationQuery",
|
"name": "VendorListPagePaginationQuery",
|
||||||
"operationKind": "query",
|
"operationKind": "query",
|
||||||
"text": "query VendorListPagePaginationQuery(\n $after: CursorKey\n $before: CursorKey\n $first: Int\n $last: Int\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...VendorListPage_vendors\n id\n }\n}\n\nfragment VendorListPage_vendors on Organization {\n id\n vendors(first: $first, after: $after, last: $last, before: $before) {\n edges {\n node {\n id\n name\n createdAt\n updatedAt\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n}\n"
|
"text": "query VendorListPagePaginationQuery(\n $after: CursorKey\n $before: CursorKey\n $first: Int\n $last: Int\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...VendorListPage_vendors\n id\n }\n}\n\nfragment VendorListPage_vendors on Organization {\n id\n vendors(first: $first, after: $after, last: $last, before: $before) {\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}\n"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
(node as any).hash = "e8f708324eb6ffe29b4f80d62179d324";
|
(node as any).hash = "b5d797660f73e909467c3ab4e988001f";
|
||||||
|
|
||||||
export default node;
|
export default node;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* @generated SignedSource<<229adb1cb5f4f80a57bb742ddae67448>>
|
* @generated SignedSource<<65ad922b0229a2d1840687c46d5cea29>>
|
||||||
* @lightSyntaxTransform
|
* @lightSyntaxTransform
|
||||||
* @nogrep
|
* @nogrep
|
||||||
*/
|
*/
|
||||||
@@ -187,6 +187,13 @@ return {
|
|||||||
"name": "name",
|
"name": "name",
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "description",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"alias": null,
|
"alias": null,
|
||||||
"args": null,
|
"args": null,
|
||||||
@@ -288,12 +295,12 @@ return {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"params": {
|
"params": {
|
||||||
"cacheID": "b910bb15ebb2e9b5aae2f20e0f5266db",
|
"cacheID": "8150b3303765174bbf41ebf4cd12534c",
|
||||||
"id": null,
|
"id": null,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"name": "VendorListPageQuery",
|
"name": "VendorListPageQuery",
|
||||||
"operationKind": "query",
|
"operationKind": "query",
|
||||||
"text": "query VendorListPageQuery(\n $first: Int\n $after: CursorKey\n $last: Int\n $before: CursorKey\n) {\n currentOrganization: node(id: \"AZSfP_xAcAC5IAAAAAAltA\") {\n __typename\n id\n ... on Organization {\n ...VendorListPage_vendors\n }\n }\n}\n\nfragment VendorListPage_vendors on Organization {\n id\n vendors(first: $first, after: $after, last: $last, before: $before) {\n edges {\n node {\n id\n name\n createdAt\n updatedAt\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n}\n"
|
"text": "query VendorListPageQuery(\n $first: Int\n $after: CursorKey\n $last: Int\n $before: CursorKey\n) {\n currentOrganization: node(id: \"AZSfP_xAcAC5IAAAAAAltA\") {\n __typename\n id\n ... on Organization {\n ...VendorListPage_vendors\n }\n }\n}\n\nfragment VendorListPage_vendors on Organization {\n id\n vendors(first: $first, after: $after, last: $last, before: $before) {\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}\n"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* @generated SignedSource<<9f827470f46558fd3946d0c714a4a713>>
|
* @generated SignedSource<<aa140c75b78a370fac81e7180d3537fc>>
|
||||||
* @lightSyntaxTransform
|
* @lightSyntaxTransform
|
||||||
* @nogrep
|
* @nogrep
|
||||||
*/
|
*/
|
||||||
@@ -17,6 +17,7 @@ export type VendorListPage_vendors$data = {
|
|||||||
readonly edges: ReadonlyArray<{
|
readonly edges: ReadonlyArray<{
|
||||||
readonly node: {
|
readonly node: {
|
||||||
readonly createdAt: any;
|
readonly createdAt: any;
|
||||||
|
readonly description: string;
|
||||||
readonly id: string;
|
readonly id: string;
|
||||||
readonly name: string;
|
readonly name: string;
|
||||||
readonly updatedAt: any;
|
readonly updatedAt: any;
|
||||||
@@ -133,6 +134,13 @@ return {
|
|||||||
"name": "name",
|
"name": "name",
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "description",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"alias": null,
|
"alias": null,
|
||||||
"args": null,
|
"args": null,
|
||||||
@@ -227,6 +235,6 @@ return {
|
|||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
(node as any).hash = "e8f708324eb6ffe29b4f80d62179d324";
|
(node as any).hash = "b5d797660f73e909467c3ab4e988001f";
|
||||||
|
|
||||||
export default node;
|
export default node;
|
||||||
|
|||||||
Reference in New Issue
Block a user