73
apps/console/src/components/PeopleSelector.tsx
Normal file
73
apps/console/src/components/PeopleSelector.tsx
Normal file
@@ -0,0 +1,73 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { graphql, useFragment } from "react-relay";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { PeopleSelector_organization$key } from "./__generated__/PeopleSelector_organization.graphql";
|
||||
|
||||
const peopleSelectorFragment = graphql`
|
||||
fragment PeopleSelector_organization on Organization {
|
||||
id
|
||||
peoples(first: 100) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
fullName
|
||||
primaryEmailAddress
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
interface PeopleSelectorProps {
|
||||
organizationRef: PeopleSelector_organization$key;
|
||||
selectedPersonId: string | null;
|
||||
onSelect: (personId: string) => void;
|
||||
placeholder?: string;
|
||||
required?: boolean;
|
||||
}
|
||||
|
||||
export default function PeopleSelector({
|
||||
organizationRef,
|
||||
selectedPersonId,
|
||||
onSelect,
|
||||
placeholder = "Select a person",
|
||||
required = false,
|
||||
}: PeopleSelectorProps) {
|
||||
const organization = useFragment(peopleSelectorFragment, organizationRef);
|
||||
const [value, setValue] = useState<string>(selectedPersonId || "");
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedPersonId) {
|
||||
setValue(selectedPersonId);
|
||||
}
|
||||
}, [selectedPersonId]);
|
||||
|
||||
const handleValueChange = (newValue: string) => {
|
||||
setValue(newValue);
|
||||
onSelect(newValue);
|
||||
};
|
||||
|
||||
return (
|
||||
<Select value={value} onValueChange={handleValueChange} required={required}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={placeholder} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{organization.peoples?.edges?.map(
|
||||
(edge) =>
|
||||
edge?.node && (
|
||||
<SelectItem key={edge.node.id} value={edge.node.id}>
|
||||
{edge.node.fullName} ({edge.node.primaryEmailAddress})
|
||||
</SelectItem>
|
||||
)
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
);
|
||||
}
|
||||
108
apps/console/src/components/__generated__/PeopleSelector_organization.graphql.ts
generated
Normal file
108
apps/console/src/components/__generated__/PeopleSelector_organization.graphql.ts
generated
Normal file
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* @generated SignedSource<<40624c6362024ecac90d44c5dd174a1c>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ReaderFragment } from 'relay-runtime';
|
||||
import { FragmentRefs } from "relay-runtime";
|
||||
export type PeopleSelector_organization$data = {
|
||||
readonly id: string;
|
||||
readonly peoples: {
|
||||
readonly edges: ReadonlyArray<{
|
||||
readonly node: {
|
||||
readonly fullName: string;
|
||||
readonly id: string;
|
||||
readonly primaryEmailAddress: string;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
readonly " $fragmentType": "PeopleSelector_organization";
|
||||
};
|
||||
export type PeopleSelector_organization$key = {
|
||||
readonly " $data"?: PeopleSelector_organization$data;
|
||||
readonly " $fragmentSpreads": FragmentRefs<"PeopleSelector_organization">;
|
||||
};
|
||||
|
||||
const node: ReaderFragment = (function(){
|
||||
var v0 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"argumentDefinitions": [],
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "PeopleSelector_organization",
|
||||
"selections": [
|
||||
(v0/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": [
|
||||
{
|
||||
"kind": "Literal",
|
||||
"name": "first",
|
||||
"value": 100
|
||||
}
|
||||
],
|
||||
"concreteType": "PeopleConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "peoples",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "PeopleEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "edges",
|
||||
"plural": true,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "People",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v0/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "fullName",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "primaryEmailAddress",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": "peoples(first:100)"
|
||||
}
|
||||
],
|
||||
"type": "Organization",
|
||||
"abstractKey": null
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "33e28b2889fa1180f6bf491e428fbafd";
|
||||
|
||||
export default node;
|
||||
@@ -1,16 +1,34 @@
|
||||
import { useState } from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
import { useNavigate, useParams } from "react-router";
|
||||
import { ConnectionHandler, graphql, useMutation } from "react-relay";
|
||||
import {
|
||||
ConnectionHandler,
|
||||
graphql,
|
||||
useMutation,
|
||||
useQueryLoader,
|
||||
usePreloadedQuery,
|
||||
PreloadedQuery,
|
||||
} from "react-relay";
|
||||
import { Helmet } from "react-helmet-async";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { FileText, Calendar } from "lucide-react";
|
||||
import { FileText, Calendar, User } from "lucide-react";
|
||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||
import PolicyEditor from "@/components/PolicyEditor";
|
||||
import PeopleSelector from "@/components/PeopleSelector";
|
||||
import { Suspense } from "react";
|
||||
import type { CreatePolicyPageMutation } from "./__generated__/CreatePolicyPageMutation.graphql";
|
||||
import type { CreatePolicyPageQuery as CreatePolicyPageQueryType } from "./__generated__/CreatePolicyPageQuery.graphql";
|
||||
|
||||
const CreatePolicyQuery = graphql`
|
||||
query CreatePolicyPageQuery($organizationId: ID!) {
|
||||
organization: node(id: $organizationId) {
|
||||
...PeopleSelector_organization
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const CreatePolicyMutation = graphql`
|
||||
mutation CreatePolicyPageMutation(
|
||||
@@ -25,19 +43,32 @@ const CreatePolicyMutation = graphql`
|
||||
content
|
||||
status
|
||||
reviewDate
|
||||
owner {
|
||||
id
|
||||
fullName
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default function CreatePolicyPage() {
|
||||
function CreatePolicyForm({
|
||||
queryRef,
|
||||
}: {
|
||||
queryRef: PreloadedQuery<CreatePolicyPageQueryType>;
|
||||
}) {
|
||||
const navigate = useNavigate();
|
||||
const { organizationId } = useParams();
|
||||
const data = usePreloadedQuery<CreatePolicyPageQueryType>(
|
||||
CreatePolicyQuery,
|
||||
queryRef
|
||||
);
|
||||
const [name, setName] = useState("");
|
||||
const [content, setContent] = useState("");
|
||||
const [status, setStatus] = useState<"DRAFT" | "ACTIVE">("DRAFT");
|
||||
const [reviewDate, setReviewDate] = useState("");
|
||||
const [ownerId, setOwnerId] = useState<string | null>(null);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const { toast } = useToast();
|
||||
|
||||
@@ -53,6 +84,16 @@ export default function CreatePolicyPage() {
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (!ownerId) {
|
||||
toast({
|
||||
title: "Error",
|
||||
description: "Please select an owner for the policy.",
|
||||
variant: "destructive",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setIsSubmitting(true);
|
||||
|
||||
// Convert reviewDate string to ISO format for the API
|
||||
@@ -67,6 +108,7 @@ export default function CreatePolicyPage() {
|
||||
content,
|
||||
status,
|
||||
reviewDate: reviewDateValue,
|
||||
ownerId,
|
||||
};
|
||||
|
||||
commitMutation({
|
||||
@@ -184,6 +226,20 @@ export default function CreatePolicyPage() {
|
||||
</RadioGroup>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="owner" className="flex items-center gap-2">
|
||||
<User className="h-4 w-4" />
|
||||
Policy Owner
|
||||
</Label>
|
||||
<PeopleSelector
|
||||
organizationRef={data.organization}
|
||||
selectedPersonId={ownerId}
|
||||
onSelect={setOwnerId}
|
||||
placeholder="Select policy owner"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label
|
||||
htmlFor="reviewDate"
|
||||
@@ -222,3 +278,38 @@ export default function CreatePolicyPage() {
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function CreatePolicyPageFallback() {
|
||||
return (
|
||||
<div className="container mx-auto py-6">
|
||||
<div className="flex items-center mb-6">
|
||||
<div className="mr-4">
|
||||
<div className="h-12 w-12 bg-muted animate-pulse rounded-lg" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="h-8 w-48 bg-muted animate-pulse rounded mb-2" />
|
||||
<div className="h-4 w-64 bg-muted animate-pulse rounded" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-muted animate-pulse rounded-lg h-[600px]" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function CreatePolicyPage() {
|
||||
const { organizationId } = useParams();
|
||||
const [queryRef, loadQuery] =
|
||||
useQueryLoader<CreatePolicyPageQueryType>(CreatePolicyQuery);
|
||||
|
||||
useEffect(() => {
|
||||
if (organizationId) {
|
||||
loadQuery({ organizationId });
|
||||
}
|
||||
}, [organizationId, loadQuery]);
|
||||
|
||||
return (
|
||||
<Suspense fallback={<CreatePolicyPageFallback />}>
|
||||
{queryRef && <CreatePolicyForm queryRef={queryRef} />}
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
usePreloadedQuery,
|
||||
useQueryLoader,
|
||||
} from "react-relay";
|
||||
import { Edit, Download, Shield, User, FileText } from "lucide-react";
|
||||
import { Edit, Download, Shield, User, FileText, Calendar } from "lucide-react";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
@@ -26,6 +26,11 @@ const PolicyOverviewPageQuery = graphql`
|
||||
updatedAt
|
||||
reviewDate
|
||||
status
|
||||
owner {
|
||||
id
|
||||
fullName
|
||||
primaryEmailAddress
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -70,205 +75,268 @@ function PolicyOverviewPageContent({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container mx-auto py-6">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||
<div className="lg:col-span-2">
|
||||
<Card className="border shadow-sm">
|
||||
<CardContent className="p-6">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="p-2 rounded-md bg-slate-100">
|
||||
<Shield className="h-6 w-6" />
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="bg-black text-white hover:bg-black/90 px-3 py-1 rounded-md font-medium"
|
||||
>
|
||||
SOC2
|
||||
</Badge>
|
||||
<Badge className="px-3 py-1 rounded-md font-medium">
|
||||
Security
|
||||
</Badge>
|
||||
{policy.status && (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>{policy.name} - Probo Console</title>
|
||||
</Helmet>
|
||||
<div className="container mx-auto py-6">
|
||||
<div className="flex justify-between items-start mb-6">
|
||||
<div className="flex items-center">
|
||||
<div className="mr-4">
|
||||
<div className="flex h-12 w-12 items-center justify-center rounded-lg bg-primary/10">
|
||||
<FileText className="h-6 w-6 text-primary" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold">{policy.name}</h1>
|
||||
<div className="flex items-center gap-2 text-muted-foreground">
|
||||
<Badge
|
||||
variant={policy.status === "ACTIVE" ? "default" : "outline"}
|
||||
>
|
||||
{policy.status === "ACTIVE" ? "Active" : "Draft"}
|
||||
</Badge>
|
||||
{policy.owner && (
|
||||
<div className="flex items-center gap-1">
|
||||
<User className="h-3 w-3" />
|
||||
<span>{policy.owner.fullName}</span>
|
||||
</div>
|
||||
)}
|
||||
{policy.reviewDate && (
|
||||
<div className="flex items-center gap-1">
|
||||
<Calendar className="h-3 w-3" />
|
||||
<span>Review: {formatDate(policy.reviewDate)}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Button variant="outline" size="sm" asChild>
|
||||
<Link
|
||||
to={`/organizations/${organizationId}/policies/${policy.id}/edit`}
|
||||
>
|
||||
<Edit className="h-4 w-4 mr-2" />
|
||||
Edit
|
||||
</Link>
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
// Logic to download policy content as PDF or text
|
||||
const element = document.createElement("a");
|
||||
const file = new Blob([policy.content || ""], {
|
||||
type: "text/plain",
|
||||
});
|
||||
element.href = URL.createObjectURL(file);
|
||||
element.download = `${policy.name}.txt`;
|
||||
document.body.appendChild(element);
|
||||
element.click();
|
||||
document.body.removeChild(element);
|
||||
}}
|
||||
>
|
||||
<Download className="h-4 w-4 mr-2" />
|
||||
Download
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||
<div className="lg:col-span-2">
|
||||
<Card className="border shadow-sm">
|
||||
<CardContent className="p-6">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="p-2 rounded-md bg-slate-100">
|
||||
<Shield className="h-6 w-6" />
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Badge
|
||||
className={`px-3 py-1 rounded-md font-medium ${
|
||||
policy.status === "ACTIVE"
|
||||
? "bg-green-100 text-green-700 hover:bg-green-200"
|
||||
variant="outline"
|
||||
className="bg-black text-white hover:bg-black/90 px-3 py-1 rounded-md font-medium"
|
||||
>
|
||||
SOC2
|
||||
</Badge>
|
||||
<Badge className="px-3 py-1 rounded-md font-medium">
|
||||
Security
|
||||
</Badge>
|
||||
{policy.status && (
|
||||
<Badge
|
||||
className={`px-3 py-1 rounded-md font-medium ${
|
||||
policy.status === "ACTIVE"
|
||||
? "bg-green-100 text-green-700 hover:bg-green-200"
|
||||
: policy.status === "DRAFT"
|
||||
? "bg-yellow-100 text-yellow-700 hover:bg-yellow-200"
|
||||
: "bg-gray-100 text-gray-700 hover:bg-gray-200"
|
||||
}`}
|
||||
>
|
||||
{policy.status === "ACTIVE"
|
||||
? "Active"
|
||||
: policy.status === "DRAFT"
|
||||
? "bg-yellow-100 text-yellow-700 hover:bg-yellow-200"
|
||||
: "bg-gray-100 text-gray-700 hover:bg-gray-200"
|
||||
? "Draft"
|
||||
: policy.status}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1 className="text-3xl font-bold mb-3">{policy.name}</h1>
|
||||
|
||||
<p className="text-muted-foreground mb-6">
|
||||
{getDescription(policy.content)}
|
||||
</p>
|
||||
|
||||
<Tabs
|
||||
defaultValue="content"
|
||||
value={activeTab}
|
||||
onValueChange={setActiveTab}
|
||||
className="mb-6"
|
||||
>
|
||||
<TabsList className="border-b w-full rounded-none bg-transparent p-0 h-auto">
|
||||
<TabsTrigger
|
||||
value="content"
|
||||
className={`rounded-none border-b-2 border-transparent px-4 py-2 font-medium ${
|
||||
activeTab === "content"
|
||||
? "border-primary text-primary"
|
||||
: "text-muted-foreground"
|
||||
}`}
|
||||
>
|
||||
{policy.status === "ACTIVE"
|
||||
? "Active"
|
||||
: policy.status === "DRAFT"
|
||||
? "Draft"
|
||||
: policy.status}
|
||||
</Badge>
|
||||
)}
|
||||
Policy Content
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="history"
|
||||
className={`rounded-none border-b-2 border-transparent px-4 py-2 font-medium ${
|
||||
activeTab === "history"
|
||||
? "border-primary text-primary"
|
||||
: "text-muted-foreground"
|
||||
}`}
|
||||
>
|
||||
Version History
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="approvals"
|
||||
className={`rounded-none border-b-2 border-transparent px-4 py-2 font-medium ${
|
||||
activeTab === "approvals"
|
||||
? "border-primary text-primary"
|
||||
: "text-muted-foreground"
|
||||
}`}
|
||||
>
|
||||
Approvals
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="content" className="pt-6">
|
||||
<div className="prose prose-sm md:prose-base lg:prose-lg max-w-none">
|
||||
<div
|
||||
className="policy-content"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: policy.content || "",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="history" className="pt-6">
|
||||
<div className="text-center py-12">
|
||||
<p className="text-muted-foreground">
|
||||
Version history will be available soon.
|
||||
</p>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="approvals" className="pt-6">
|
||||
<div className="text-center py-12">
|
||||
<p className="text-muted-foreground">
|
||||
Approval workflow will be available soon.
|
||||
</p>
|
||||
</div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div className="lg:col-span-1">
|
||||
<Card className="border shadow-sm mb-6">
|
||||
<CardContent className="p-6">
|
||||
<h2 className="text-xl font-semibold mb-6">Policy Details</h2>
|
||||
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<div className="flex items-center gap-2 text-muted-foreground mb-1">
|
||||
<FileText className="h-4 w-4" />
|
||||
<span className="text-sm">Last Updated</span>
|
||||
</div>
|
||||
<p className="font-medium">
|
||||
{formatDate(policy.updatedAt)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="flex items-center gap-2 text-muted-foreground mb-1">
|
||||
<FileText className="h-4 w-4" />
|
||||
<span className="text-sm">Review Due</span>
|
||||
</div>
|
||||
<p className="font-medium">
|
||||
{policy.reviewDate
|
||||
? formatDate(policy.reviewDate)
|
||||
: "Not set"}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="flex items-center gap-2 text-muted-foreground mb-1">
|
||||
<User className="h-4 w-4" />
|
||||
<span className="text-sm">Owner</span>
|
||||
</div>
|
||||
<p className="font-medium">
|
||||
{policy.owner ? policy.owner.fullName : "Not assigned"}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="ml-auto">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="rounded-full"
|
||||
>
|
||||
<Download className="h-5 w-5" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<h1 className="text-3xl font-bold mb-3">{policy.name}</h1>
|
||||
|
||||
<p className="text-muted-foreground mb-6">
|
||||
{getDescription(policy.content)}
|
||||
</p>
|
||||
|
||||
<Tabs
|
||||
defaultValue="content"
|
||||
value={activeTab}
|
||||
onValueChange={setActiveTab}
|
||||
className="mb-6"
|
||||
<Button asChild className="w-full">
|
||||
<Link
|
||||
to={`/organizations/${organizationId}/policies/${policy.id}/update`}
|
||||
>
|
||||
<TabsList className="border-b w-full rounded-none bg-transparent p-0 h-auto">
|
||||
<TabsTrigger
|
||||
value="content"
|
||||
className={`rounded-none border-b-2 border-transparent px-4 py-2 font-medium ${
|
||||
activeTab === "content"
|
||||
? "border-primary text-primary"
|
||||
: "text-muted-foreground"
|
||||
}`}
|
||||
<Edit className="mr-2 h-4 w-4" />
|
||||
Edit Policy
|
||||
</Link>
|
||||
</Button>
|
||||
|
||||
<Card className="border shadow-sm mt-6 bg-red-50">
|
||||
<CardContent className="p-6">
|
||||
<h3 className="text-red-500 font-semibold mb-3">Danger Zone</h3>
|
||||
<p className="text-sm text-muted-foreground mb-4">
|
||||
Permanently delete this policy and all of its data. This
|
||||
action cannot be undone.
|
||||
</p>
|
||||
<Button variant="destructive" className="w-full">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className="mr-2"
|
||||
>
|
||||
Policy Content
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="history"
|
||||
className={`rounded-none border-b-2 border-transparent px-4 py-2 font-medium ${
|
||||
activeTab === "history"
|
||||
? "border-primary text-primary"
|
||||
: "text-muted-foreground"
|
||||
}`}
|
||||
>
|
||||
Version History
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="approvals"
|
||||
className={`rounded-none border-b-2 border-transparent px-4 py-2 font-medium ${
|
||||
activeTab === "approvals"
|
||||
? "border-primary text-primary"
|
||||
: "text-muted-foreground"
|
||||
}`}
|
||||
>
|
||||
Approvals
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="content" className="pt-6">
|
||||
<div className="prose prose-sm md:prose-base lg:prose-lg max-w-none">
|
||||
<div
|
||||
className="policy-content"
|
||||
dangerouslySetInnerHTML={{ __html: policy.content || "" }}
|
||||
/>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="history" className="pt-6">
|
||||
<div className="text-center py-12">
|
||||
<p className="text-muted-foreground">
|
||||
Version history will be available soon.
|
||||
</p>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="approvals" className="pt-6">
|
||||
<div className="text-center py-12">
|
||||
<p className="text-muted-foreground">
|
||||
Approval workflow will be available soon.
|
||||
</p>
|
||||
</div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div className="lg:col-span-1">
|
||||
<Card className="border shadow-sm mb-6">
|
||||
<CardContent className="p-6">
|
||||
<h2 className="text-xl font-semibold mb-6">Policy Details</h2>
|
||||
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<div className="flex items-center gap-2 text-muted-foreground mb-1">
|
||||
<FileText className="h-4 w-4" />
|
||||
<span className="text-sm">Last Updated</span>
|
||||
</div>
|
||||
<p className="font-medium">{formatDate(policy.updatedAt)}</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="flex items-center gap-2 text-muted-foreground mb-1">
|
||||
<FileText className="h-4 w-4" />
|
||||
<span className="text-sm">Review Due</span>
|
||||
</div>
|
||||
<p className="font-medium">
|
||||
{policy.reviewDate
|
||||
? formatDate(policy.reviewDate)
|
||||
: "Not set"}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="flex items-center gap-2 text-muted-foreground mb-1">
|
||||
<User className="h-4 w-4" />
|
||||
<span className="text-sm">Owner</span>
|
||||
</div>
|
||||
<p className="font-medium">Jane Smith, CISO</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Button asChild className="w-full">
|
||||
<Link
|
||||
to={`/organizations/${organizationId}/policies/${policy.id}/update`}
|
||||
>
|
||||
<Edit className="mr-2 h-4 w-4" />
|
||||
Edit Policy
|
||||
</Link>
|
||||
</Button>
|
||||
|
||||
<Card className="border shadow-sm mt-6 bg-red-50">
|
||||
<CardContent className="p-6">
|
||||
<h3 className="text-red-500 font-semibold mb-3">Danger Zone</h3>
|
||||
<p className="text-sm text-muted-foreground mb-4">
|
||||
Permanently delete this policy and all of its data. This action
|
||||
cannot be undone.
|
||||
</p>
|
||||
<Button variant="destructive" className="w-full">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className="mr-2"
|
||||
>
|
||||
<path d="M3 6h18"></path>
|
||||
<path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"></path>
|
||||
<path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"></path>
|
||||
</svg>
|
||||
Delete Policy
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<path d="M3 6h18"></path>
|
||||
<path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"></path>
|
||||
<path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"></path>
|
||||
</svg>
|
||||
Delete Policy
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -377,13 +445,8 @@ export default function PolicyOverviewPage() {
|
||||
}, [loadQuery, policyId]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>Policy Details - Probo Console</title>
|
||||
</Helmet>
|
||||
<Suspense fallback={<PolicyOverviewPageFallback />}>
|
||||
{queryRef && <PolicyOverviewPageContent queryRef={queryRef} />}
|
||||
</Suspense>
|
||||
</>
|
||||
<Suspense fallback={<PolicyOverviewPageFallback />}>
|
||||
{queryRef && <PolicyOverviewPageContent queryRef={queryRef} />}
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -13,16 +13,17 @@ import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { toast } from "@/hooks/use-toast";
|
||||
import { FileText, Calendar } from "lucide-react";
|
||||
import { FileText, Calendar, User } from "lucide-react";
|
||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||
import { Suspense } from "react";
|
||||
import PolicyEditor from "@/components/PolicyEditor";
|
||||
import PeopleSelector from "@/components/PeopleSelector";
|
||||
import type { UpdatePolicyPageQuery as UpdatePolicyPageQueryType } from "./__generated__/UpdatePolicyPageQuery.graphql";
|
||||
import type { UpdatePolicyPageMutation as UpdatePolicyPageMutationType } from "./__generated__/UpdatePolicyPageMutation.graphql";
|
||||
|
||||
const UpdatePolicyPageQuery = graphql`
|
||||
query UpdatePolicyPageQuery($policyId: ID!) {
|
||||
node(id: $policyId) {
|
||||
query UpdatePolicyPageQuery($policyId: ID!, $organizationId: ID!) {
|
||||
policy: node(id: $policyId) {
|
||||
id
|
||||
... on Policy {
|
||||
name
|
||||
@@ -30,8 +31,15 @@ const UpdatePolicyPageQuery = graphql`
|
||||
status
|
||||
version
|
||||
reviewDate
|
||||
owner {
|
||||
id
|
||||
fullName
|
||||
}
|
||||
}
|
||||
}
|
||||
organization: node(id: $organizationId) {
|
||||
...PeopleSelector_organization
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -45,6 +53,10 @@ const UpdatePolicyMutation = graphql`
|
||||
status
|
||||
version
|
||||
reviewDate
|
||||
owner {
|
||||
id
|
||||
fullName
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,12 +74,15 @@ function UpdatePolicyPageContent({
|
||||
queryRef
|
||||
);
|
||||
|
||||
console.log("UpdatePolicyPage data:", data.node);
|
||||
console.log("UpdatePolicyPage data:", data.policy);
|
||||
|
||||
const [name, setName] = useState(data.node.name);
|
||||
const [content, setContent] = useState(data.node.content || "");
|
||||
const [status, setStatus] = useState(data.node.status);
|
||||
const [reviewDate, setReviewDate] = useState(data.node.reviewDate || "");
|
||||
const [name, setName] = useState(data.policy.name);
|
||||
const [content, setContent] = useState(data.policy.content || "");
|
||||
const [status, setStatus] = useState(data.policy.status);
|
||||
const [reviewDate, setReviewDate] = useState(data.policy.reviewDate || "");
|
||||
const [ownerId, setOwnerId] = useState<string | null>(
|
||||
data.policy.owner?.id || null
|
||||
);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
console.log(
|
||||
@@ -93,12 +108,13 @@ function UpdatePolicyPageContent({
|
||||
commitMutation({
|
||||
variables: {
|
||||
input: {
|
||||
id: data.node.id,
|
||||
id: data.policy.id,
|
||||
name,
|
||||
content,
|
||||
status,
|
||||
reviewDate: reviewDateValue,
|
||||
expectedVersion: data.node.version!,
|
||||
ownerId,
|
||||
expectedVersion: data.policy.version!,
|
||||
},
|
||||
},
|
||||
onCompleted: (response, errors) => {
|
||||
@@ -200,6 +216,20 @@ function UpdatePolicyPageContent({
|
||||
</RadioGroup>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="owner" className="flex items-center gap-2">
|
||||
<User className="h-4 w-4" />
|
||||
Policy Owner
|
||||
</Label>
|
||||
<PeopleSelector
|
||||
organizationRef={data.organization}
|
||||
selectedPersonId={ownerId}
|
||||
onSelect={setOwnerId}
|
||||
placeholder="Select policy owner"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label
|
||||
htmlFor="reviewDate"
|
||||
@@ -253,48 +283,26 @@ function UpdatePolicyPageFallback() {
|
||||
<div className="h-4 w-64 bg-muted animate-pulse rounded" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-6">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<div className="h-6 w-32 bg-muted animate-pulse rounded" />
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
{[1, 2, 3, 4].map((i) => (
|
||||
<div key={i} className="space-y-2">
|
||||
<div className="h-4 w-24 bg-muted animate-pulse rounded" />
|
||||
<div className="h-10 w-full bg-muted animate-pulse rounded" />
|
||||
</div>
|
||||
))}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
<div className="bg-muted animate-pulse rounded-lg h-[600px]" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function UpdatePolicyPage() {
|
||||
const { organizationId, policyId } = useParams();
|
||||
const [queryRef, loadQuery] = useQueryLoader<UpdatePolicyPageQueryType>(
|
||||
UpdatePolicyPageQuery
|
||||
);
|
||||
const { policyId } = useParams();
|
||||
|
||||
useEffect(() => {
|
||||
loadQuery({ policyId: policyId! });
|
||||
}, [loadQuery, policyId]);
|
||||
|
||||
if (!queryRef) {
|
||||
return <UpdatePolicyPageFallback />;
|
||||
}
|
||||
if (organizationId && policyId) {
|
||||
loadQuery({ organizationId, policyId });
|
||||
}
|
||||
}, [organizationId, policyId, loadQuery]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>Update Policy - Probo Console</title>
|
||||
</Helmet>
|
||||
<Suspense fallback={<UpdatePolicyPageFallback />}>
|
||||
<UpdatePolicyPageContent queryRef={queryRef} />
|
||||
</Suspense>
|
||||
</>
|
||||
<Suspense fallback={<UpdatePolicyPageFallback />}>
|
||||
{queryRef && <UpdatePolicyPageContent queryRef={queryRef} />}
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<d242fdb748915009dc461be5596b6fc1>>
|
||||
* @generated SignedSource<<29caed5a50be912ff48f9c131ce4045b>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -14,6 +14,7 @@ export type CreatePolicyInput = {
|
||||
content: string;
|
||||
name: string;
|
||||
organizationId: string;
|
||||
ownerId: string;
|
||||
reviewDate?: string | null | undefined;
|
||||
status: PolicyStatus;
|
||||
};
|
||||
@@ -28,6 +29,10 @@ export type CreatePolicyPageMutation$data = {
|
||||
readonly content: string;
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly owner: {
|
||||
readonly fullName: string;
|
||||
readonly id: string;
|
||||
};
|
||||
readonly reviewDate: string | null | undefined;
|
||||
readonly status: PolicyStatus;
|
||||
};
|
||||
@@ -58,6 +63,13 @@ v2 = [
|
||||
}
|
||||
],
|
||||
v3 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
v4 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "PolicyEdge",
|
||||
@@ -73,13 +85,7 @@ v3 = {
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
(v3/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
@@ -107,6 +113,25 @@ v3 = {
|
||||
"kind": "ScalarField",
|
||||
"name": "reviewDate",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "People",
|
||||
"kind": "LinkedField",
|
||||
"name": "owner",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "fullName",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
@@ -132,7 +157,7 @@ return {
|
||||
"name": "createPolicy",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/)
|
||||
(v4/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
@@ -157,7 +182,7 @@ return {
|
||||
"name": "createPolicy",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/),
|
||||
(v4/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
@@ -180,16 +205,16 @@ return {
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "64796b84575bf926f45da4c98d31e645",
|
||||
"cacheID": "cf17928879d0f2b2e68367ed2c132d0a",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "CreatePolicyPageMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation CreatePolicyPageMutation(\n $input: CreatePolicyInput!\n) {\n createPolicy(input: $input) {\n policyEdge {\n node {\n id\n name\n content\n status\n reviewDate\n }\n }\n }\n}\n"
|
||||
"text": "mutation CreatePolicyPageMutation(\n $input: CreatePolicyInput!\n) {\n createPolicy(input: $input) {\n policyEdge {\n node {\n id\n name\n content\n status\n reviewDate\n owner {\n id\n fullName\n }\n }\n }\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "b7ecb61ced1e31c0c0093946135eca3b";
|
||||
(node as any).hash = "1ddb9b7010ecf1fe55e2b56aaa034295";
|
||||
|
||||
export default node;
|
||||
|
||||
176
apps/console/src/pages/__generated__/CreatePolicyPageQuery.graphql.ts
generated
Normal file
176
apps/console/src/pages/__generated__/CreatePolicyPageQuery.graphql.ts
generated
Normal file
@@ -0,0 +1,176 @@
|
||||
/**
|
||||
* @generated SignedSource<<0229a3e8e814cf3971479aa5abc20322>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
import { FragmentRefs } from "relay-runtime";
|
||||
export type CreatePolicyPageQuery$variables = {
|
||||
organizationId: string;
|
||||
};
|
||||
export type CreatePolicyPageQuery$data = {
|
||||
readonly organization: {
|
||||
readonly " $fragmentSpreads": FragmentRefs<"PeopleSelector_organization">;
|
||||
};
|
||||
};
|
||||
export type CreatePolicyPageQuery = {
|
||||
response: CreatePolicyPageQuery$data;
|
||||
variables: CreatePolicyPageQuery$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "organizationId"
|
||||
}
|
||||
],
|
||||
v1 = [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "id",
|
||||
"variableName": "organizationId"
|
||||
}
|
||||
],
|
||||
v2 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "CreatePolicyPageQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": "organization",
|
||||
"args": (v1/*: any*/),
|
||||
"concreteType": null,
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"args": null,
|
||||
"kind": "FragmentSpread",
|
||||
"name": "PeopleSelector_organization"
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Query",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "CreatePolicyPageQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": "organization",
|
||||
"args": (v1/*: any*/),
|
||||
"concreteType": null,
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "__typename",
|
||||
"storageKey": null
|
||||
},
|
||||
(v2/*: any*/),
|
||||
{
|
||||
"kind": "InlineFragment",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": [
|
||||
{
|
||||
"kind": "Literal",
|
||||
"name": "first",
|
||||
"value": 100
|
||||
}
|
||||
],
|
||||
"concreteType": "PeopleConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "peoples",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "PeopleEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "edges",
|
||||
"plural": true,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "People",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "fullName",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "primaryEmailAddress",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": "peoples(first:100)"
|
||||
}
|
||||
],
|
||||
"type": "Organization",
|
||||
"abstractKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "edb18dbe0439fa7ca7bc791e607cd447",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "CreatePolicyPageQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query CreatePolicyPageQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n ...PeopleSelector_organization\n id\n }\n}\n\nfragment PeopleSelector_organization on Organization {\n id\n peoples(first: 100) {\n edges {\n node {\n id\n fullName\n primaryEmailAddress\n }\n }\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "9ab69c4bfcba5c896760f297ab38ff51";
|
||||
|
||||
export default node;
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<ebffaaa37d8815b22f9fa75aca6e75d2>>
|
||||
* @generated SignedSource<<a49c4b1ca92e109b1091a1431fe0c871>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -19,11 +19,11 @@ export type PolicyListPageQuery$data = {
|
||||
readonly edges: ReadonlyArray<{
|
||||
readonly node: {
|
||||
readonly content: string;
|
||||
readonly createdAt: any;
|
||||
readonly createdAt: string;
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly status: PolicyStatus;
|
||||
readonly updatedAt: any;
|
||||
readonly updatedAt: string;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<0eb945d3cbf8f223621fe7aa2b93eddc>>
|
||||
* @generated SignedSource<<67ee63bc3751efdd9ecdf2ad9ab5e197>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -19,6 +19,11 @@ export type PolicyOverviewPageQuery$data = {
|
||||
readonly createdAt?: string;
|
||||
readonly id: string;
|
||||
readonly name?: string;
|
||||
readonly owner?: {
|
||||
readonly fullName: string;
|
||||
readonly id: string;
|
||||
readonly primaryEmailAddress: string;
|
||||
};
|
||||
readonly reviewDate?: string | null | undefined;
|
||||
readonly status?: PolicyStatus;
|
||||
readonly updatedAt?: string;
|
||||
@@ -95,6 +100,32 @@ v3 = {
|
||||
"kind": "ScalarField",
|
||||
"name": "status",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "People",
|
||||
"kind": "LinkedField",
|
||||
"name": "owner",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "fullName",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "primaryEmailAddress",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Policy",
|
||||
@@ -153,16 +184,16 @@ return {
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "d3b31142132a379f9c3a8812ddcfb7da",
|
||||
"cacheID": "c57fd1a2ff5ffa776bebb9f15bd534bc",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "PolicyOverviewPageQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query PolicyOverviewPageQuery(\n $policyId: ID!\n) {\n node(id: $policyId) {\n __typename\n id\n ... on Policy {\n name\n content\n createdAt\n updatedAt\n reviewDate\n status\n }\n }\n}\n"
|
||||
"text": "query PolicyOverviewPageQuery(\n $policyId: ID!\n) {\n node(id: $policyId) {\n __typename\n id\n ... on Policy {\n name\n content\n createdAt\n updatedAt\n reviewDate\n status\n owner {\n id\n fullName\n primaryEmailAddress\n }\n }\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "1519b589fe7da5a258621ad445970ca7";
|
||||
(node as any).hash = "e6c4f286358df61d18ea6deb367d7182";
|
||||
|
||||
export default node;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<9a54ece29b9ff9a2efa624f67f40f803>>
|
||||
* @generated SignedSource<<f37225ac5a4b6774dadbd4f23c3bbfe9>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -15,6 +15,7 @@ export type UpdatePolicyInput = {
|
||||
expectedVersion: number;
|
||||
id: string;
|
||||
name?: string | null | undefined;
|
||||
ownerId?: string | null | undefined;
|
||||
reviewDate?: string | null | undefined;
|
||||
status?: PolicyStatus | null | undefined;
|
||||
};
|
||||
@@ -27,6 +28,10 @@ export type UpdatePolicyPageMutation$data = {
|
||||
readonly content: string;
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly owner: {
|
||||
readonly fullName: string;
|
||||
readonly id: string;
|
||||
};
|
||||
readonly reviewDate: string | null | undefined;
|
||||
readonly status: PolicyStatus;
|
||||
readonly version: number;
|
||||
@@ -46,7 +51,14 @@ var v0 = [
|
||||
"name": "input"
|
||||
}
|
||||
],
|
||||
v1 = [
|
||||
v1 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
v2 = [
|
||||
{
|
||||
"alias": null,
|
||||
"args": [
|
||||
@@ -69,13 +81,7 @@ v1 = [
|
||||
"name": "policy",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
(v1/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
@@ -110,6 +116,25 @@ v1 = [
|
||||
"kind": "ScalarField",
|
||||
"name": "reviewDate",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "People",
|
||||
"kind": "LinkedField",
|
||||
"name": "owner",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v1/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "fullName",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
@@ -124,7 +149,7 @@ return {
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "UpdatePolicyPageMutation",
|
||||
"selections": (v1/*: any*/),
|
||||
"selections": (v2/*: any*/),
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
},
|
||||
@@ -133,19 +158,19 @@ return {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "UpdatePolicyPageMutation",
|
||||
"selections": (v1/*: any*/)
|
||||
"selections": (v2/*: any*/)
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "ce2ccf9449b52e3df3b2b9bb82c55166",
|
||||
"cacheID": "cab3ba1fb88944fade60b5d6a0e0b7b9",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "UpdatePolicyPageMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation UpdatePolicyPageMutation(\n $input: UpdatePolicyInput!\n) {\n updatePolicy(input: $input) {\n policy {\n id\n name\n content\n status\n version\n reviewDate\n }\n }\n}\n"
|
||||
"text": "mutation UpdatePolicyPageMutation(\n $input: UpdatePolicyInput!\n) {\n updatePolicy(input: $input) {\n policy {\n id\n name\n content\n status\n version\n reviewDate\n owner {\n id\n fullName\n }\n }\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "d5329966d0e13b8931d6f01d9afb74e2";
|
||||
(node as any).hash = "51e4a0e297540f4e83dcb6d809430ac7";
|
||||
|
||||
export default node;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<5ee387b238e8fdc7c41851c7e6740c95>>
|
||||
* @generated SignedSource<<04e2a37d31d4cd204101efdd85471dc5>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -9,15 +9,24 @@
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
import { FragmentRefs } from "relay-runtime";
|
||||
export type PolicyStatus = "ACTIVE" | "DRAFT";
|
||||
export type UpdatePolicyPageQuery$variables = {
|
||||
organizationId: string;
|
||||
policyId: string;
|
||||
};
|
||||
export type UpdatePolicyPageQuery$data = {
|
||||
readonly node: {
|
||||
readonly organization: {
|
||||
readonly " $fragmentSpreads": FragmentRefs<"PeopleSelector_organization">;
|
||||
};
|
||||
readonly policy: {
|
||||
readonly content?: string;
|
||||
readonly id: string;
|
||||
readonly name?: string;
|
||||
readonly owner?: {
|
||||
readonly fullName: string;
|
||||
readonly id: string;
|
||||
};
|
||||
readonly reviewDate?: string | null | undefined;
|
||||
readonly status?: PolicyStatus;
|
||||
readonly version?: number;
|
||||
@@ -29,28 +38,38 @@ export type UpdatePolicyPageQuery = {
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "policyId"
|
||||
}
|
||||
],
|
||||
v1 = [
|
||||
var v0 = {
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "organizationId"
|
||||
},
|
||||
v1 = {
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "policyId"
|
||||
},
|
||||
v2 = [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "id",
|
||||
"variableName": "policyId"
|
||||
}
|
||||
],
|
||||
v2 = {
|
||||
v3 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
v3 = {
|
||||
v4 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "fullName",
|
||||
"storageKey": null
|
||||
},
|
||||
v5 = {
|
||||
"kind": "InlineFragment",
|
||||
"selections": [
|
||||
{
|
||||
@@ -87,28 +106,74 @@ v3 = {
|
||||
"kind": "ScalarField",
|
||||
"name": "reviewDate",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "People",
|
||||
"kind": "LinkedField",
|
||||
"name": "owner",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/),
|
||||
(v4/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Policy",
|
||||
"abstractKey": null
|
||||
},
|
||||
v6 = [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "id",
|
||||
"variableName": "organizationId"
|
||||
}
|
||||
],
|
||||
v7 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "__typename",
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"argumentDefinitions": [
|
||||
(v0/*: any*/),
|
||||
(v1/*: any*/)
|
||||
],
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "UpdatePolicyPageQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v1/*: any*/),
|
||||
"alias": "policy",
|
||||
"args": (v2/*: any*/),
|
||||
"concreteType": null,
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/)
|
||||
(v3/*: any*/),
|
||||
(v5/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": "organization",
|
||||
"args": (v6/*: any*/),
|
||||
"concreteType": null,
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"args": null,
|
||||
"kind": "FragmentSpread",
|
||||
"name": "PeopleSelector_organization"
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
@@ -118,43 +183,108 @@ return {
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"argumentDefinitions": [
|
||||
(v1/*: any*/),
|
||||
(v0/*: any*/)
|
||||
],
|
||||
"kind": "Operation",
|
||||
"name": "UpdatePolicyPageQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v1/*: any*/),
|
||||
"alias": "policy",
|
||||
"args": (v2/*: any*/),
|
||||
"concreteType": null,
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v7/*: any*/),
|
||||
(v3/*: any*/),
|
||||
(v5/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": "organization",
|
||||
"args": (v6/*: any*/),
|
||||
"concreteType": null,
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v7/*: any*/),
|
||||
(v3/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "__typename",
|
||||
"storageKey": null
|
||||
},
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/)
|
||||
"kind": "InlineFragment",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": [
|
||||
{
|
||||
"kind": "Literal",
|
||||
"name": "first",
|
||||
"value": 100
|
||||
}
|
||||
],
|
||||
"concreteType": "PeopleConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "peoples",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "PeopleEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "edges",
|
||||
"plural": true,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "People",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/),
|
||||
(v4/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "primaryEmailAddress",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": "peoples(first:100)"
|
||||
}
|
||||
],
|
||||
"type": "Organization",
|
||||
"abstractKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "fc062f2d2fcce04954feb69436433093",
|
||||
"cacheID": "abbd1265e5fbacc34d5442772cbd645b",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "UpdatePolicyPageQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query UpdatePolicyPageQuery(\n $policyId: ID!\n) {\n node(id: $policyId) {\n __typename\n id\n ... on Policy {\n name\n content\n status\n version\n reviewDate\n }\n }\n}\n"
|
||||
"text": "query UpdatePolicyPageQuery(\n $policyId: ID!\n $organizationId: ID!\n) {\n policy: node(id: $policyId) {\n __typename\n id\n ... on Policy {\n name\n content\n status\n version\n reviewDate\n owner {\n id\n fullName\n }\n }\n }\n organization: node(id: $organizationId) {\n __typename\n ...PeopleSelector_organization\n id\n }\n}\n\nfragment PeopleSelector_organization on Organization {\n id\n peoples(first: 100) {\n edges {\n node {\n id\n fullName\n primaryEmailAddress\n }\n }\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "4ae49cad4f03a09d87475483c271bd94";
|
||||
(node as any).hash = "abba76ee5208701e443130d650b5c8d1";
|
||||
|
||||
export default node;
|
||||
|
||||
Reference in New Issue
Block a user