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;
|
||||
|
||||
@@ -650,6 +650,7 @@ input CreatePolicyInput {
|
||||
content: String!
|
||||
status: PolicyStatus!
|
||||
reviewDate: Datetime
|
||||
ownerId: ID!
|
||||
}
|
||||
|
||||
input UpdatePolicyInput {
|
||||
@@ -659,6 +660,7 @@ input UpdatePolicyInput {
|
||||
content: String
|
||||
status: PolicyStatus
|
||||
reviewDate: Datetime
|
||||
ownerId: ID
|
||||
}
|
||||
|
||||
input DeletePolicyInput {
|
||||
@@ -684,6 +686,7 @@ type Policy implements Node {
|
||||
status: PolicyStatus!
|
||||
content: String!
|
||||
reviewDate: Datetime
|
||||
owner: People! @goField(forceResolver: true)
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ type ResolverRoot interface {
|
||||
Framework() FrameworkResolver
|
||||
Mutation() MutationResolver
|
||||
Organization() OrganizationResolver
|
||||
Policy() PolicyResolver
|
||||
Query() QueryResolver
|
||||
Task() TaskResolver
|
||||
User() UserResolver
|
||||
@@ -289,6 +290,7 @@ type ComplexityRoot struct {
|
||||
CreatedAt func(childComplexity int) int
|
||||
ID func(childComplexity int) int
|
||||
Name func(childComplexity int) int
|
||||
Owner func(childComplexity int) int
|
||||
ReviewDate func(childComplexity int) int
|
||||
Status func(childComplexity int) int
|
||||
UpdatedAt func(childComplexity int) int
|
||||
@@ -459,6 +461,9 @@ type OrganizationResolver interface {
|
||||
Peoples(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.PeopleConnection, error)
|
||||
Policies(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.PolicyConnection, error)
|
||||
}
|
||||
type PolicyResolver interface {
|
||||
Owner(ctx context.Context, obj *types.Policy) (*types.People, error)
|
||||
}
|
||||
type QueryResolver interface {
|
||||
Node(ctx context.Context, id gid.GID) (types.Node, error)
|
||||
Viewer(ctx context.Context) (*types.User, error)
|
||||
@@ -1498,6 +1503,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||
|
||||
return e.complexity.Policy.Name(childComplexity), true
|
||||
|
||||
case "Policy.owner":
|
||||
if e.complexity.Policy.Owner == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.Policy.Owner(childComplexity), true
|
||||
|
||||
case "Policy.reviewDate":
|
||||
if e.complexity.Policy.ReviewDate == nil {
|
||||
break
|
||||
@@ -2743,6 +2755,7 @@ input CreatePolicyInput {
|
||||
content: String!
|
||||
status: PolicyStatus!
|
||||
reviewDate: Datetime
|
||||
ownerId: ID!
|
||||
}
|
||||
|
||||
input UpdatePolicyInput {
|
||||
@@ -2752,6 +2765,7 @@ input UpdatePolicyInput {
|
||||
content: String
|
||||
status: PolicyStatus
|
||||
reviewDate: Datetime
|
||||
ownerId: ID
|
||||
}
|
||||
|
||||
input DeletePolicyInput {
|
||||
@@ -2777,6 +2791,7 @@ type Policy implements Node {
|
||||
status: PolicyStatus!
|
||||
content: String!
|
||||
reviewDate: Datetime
|
||||
owner: People! @goField(forceResolver: true)
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
}
|
||||
@@ -9518,6 +9533,62 @@ func (ec *executionContext) fieldContext_Policy_reviewDate(_ context.Context, fi
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Policy_owner(ctx context.Context, field graphql.CollectedField, obj *types.Policy) (ret graphql.Marshaler) {
|
||||
fc, err := ec.fieldContext_Policy_owner(ctx, field)
|
||||
if err != nil {
|
||||
return graphql.Null
|
||||
}
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return ec.resolvers.Policy().Owner(rctx, obj)
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
if !graphql.HasFieldError(ctx, fc) {
|
||||
ec.Errorf(ctx, "must not be null")
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(*types.People)
|
||||
fc.Result = res
|
||||
return ec.marshalNPeople2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋapiᚋconsoleᚋv1ᚋtypesᚐPeople(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) fieldContext_Policy_owner(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
fc = &graphql.FieldContext{
|
||||
Object: "Policy",
|
||||
Field: field,
|
||||
IsMethod: true,
|
||||
IsResolver: true,
|
||||
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||
switch field.Name {
|
||||
case "id":
|
||||
return ec.fieldContext_People_id(ctx, field)
|
||||
case "fullName":
|
||||
return ec.fieldContext_People_fullName(ctx, field)
|
||||
case "primaryEmailAddress":
|
||||
return ec.fieldContext_People_primaryEmailAddress(ctx, field)
|
||||
case "additionalEmailAddresses":
|
||||
return ec.fieldContext_People_additionalEmailAddresses(ctx, field)
|
||||
case "kind":
|
||||
return ec.fieldContext_People_kind(ctx, field)
|
||||
case "createdAt":
|
||||
return ec.fieldContext_People_createdAt(ctx, field)
|
||||
case "updatedAt":
|
||||
return ec.fieldContext_People_updatedAt(ctx, field)
|
||||
case "version":
|
||||
return ec.fieldContext_People_version(ctx, field)
|
||||
}
|
||||
return nil, fmt.Errorf("no field named %q was found under type People", field.Name)
|
||||
},
|
||||
}
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Policy_createdAt(ctx context.Context, field graphql.CollectedField, obj *types.Policy) (ret graphql.Marshaler) {
|
||||
fc, err := ec.fieldContext_Policy_createdAt(ctx, field)
|
||||
if err != nil {
|
||||
@@ -9769,6 +9840,8 @@ func (ec *executionContext) fieldContext_PolicyEdge_node(_ context.Context, fiel
|
||||
return ec.fieldContext_Policy_content(ctx, field)
|
||||
case "reviewDate":
|
||||
return ec.fieldContext_Policy_reviewDate(ctx, field)
|
||||
case "owner":
|
||||
return ec.fieldContext_Policy_owner(ctx, field)
|
||||
case "createdAt":
|
||||
return ec.fieldContext_Policy_createdAt(ctx, field)
|
||||
case "updatedAt":
|
||||
@@ -11193,6 +11266,8 @@ func (ec *executionContext) fieldContext_UpdatePolicyPayload_policy(_ context.Co
|
||||
return ec.fieldContext_Policy_content(ctx, field)
|
||||
case "reviewDate":
|
||||
return ec.fieldContext_Policy_reviewDate(ctx, field)
|
||||
case "owner":
|
||||
return ec.fieldContext_Policy_owner(ctx, field)
|
||||
case "createdAt":
|
||||
return ec.fieldContext_Policy_createdAt(ctx, field)
|
||||
case "updatedAt":
|
||||
@@ -14016,7 +14091,7 @@ func (ec *executionContext) unmarshalInputCreatePolicyInput(ctx context.Context,
|
||||
asMap[k] = v
|
||||
}
|
||||
|
||||
fieldsInOrder := [...]string{"organizationId", "name", "content", "status", "reviewDate"}
|
||||
fieldsInOrder := [...]string{"organizationId", "name", "content", "status", "reviewDate", "ownerId"}
|
||||
for _, k := range fieldsInOrder {
|
||||
v, ok := asMap[k]
|
||||
if !ok {
|
||||
@@ -14058,6 +14133,13 @@ func (ec *executionContext) unmarshalInputCreatePolicyInput(ctx context.Context,
|
||||
return it, err
|
||||
}
|
||||
it.ReviewDate = data
|
||||
case "ownerId":
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("ownerId"))
|
||||
data, err := ec.unmarshalNID2githubᚗcomᚋgetproboᚋproboᚋpkgᚋgidᚐGID(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
it.OwnerID = data
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14536,7 +14618,7 @@ func (ec *executionContext) unmarshalInputUpdatePolicyInput(ctx context.Context,
|
||||
asMap[k] = v
|
||||
}
|
||||
|
||||
fieldsInOrder := [...]string{"id", "expectedVersion", "name", "content", "status", "reviewDate"}
|
||||
fieldsInOrder := [...]string{"id", "expectedVersion", "name", "content", "status", "reviewDate", "ownerId"}
|
||||
for _, k := range fieldsInOrder {
|
||||
v, ok := asMap[k]
|
||||
if !ok {
|
||||
@@ -14585,6 +14667,13 @@ func (ec *executionContext) unmarshalInputUpdatePolicyInput(ctx context.Context,
|
||||
return it, err
|
||||
}
|
||||
it.ReviewDate = data
|
||||
case "ownerId":
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("ownerId"))
|
||||
data, err := ec.unmarshalOID2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋgidᚐGID(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
it.OwnerID = data
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16945,39 +17034,70 @@ func (ec *executionContext) _Policy(ctx context.Context, sel ast.SelectionSet, o
|
||||
case "id":
|
||||
out.Values[i] = ec._Policy_id(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
out.Invalids++
|
||||
atomic.AddUint32(&out.Invalids, 1)
|
||||
}
|
||||
case "version":
|
||||
out.Values[i] = ec._Policy_version(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
out.Invalids++
|
||||
atomic.AddUint32(&out.Invalids, 1)
|
||||
}
|
||||
case "name":
|
||||
out.Values[i] = ec._Policy_name(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
out.Invalids++
|
||||
atomic.AddUint32(&out.Invalids, 1)
|
||||
}
|
||||
case "status":
|
||||
out.Values[i] = ec._Policy_status(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
out.Invalids++
|
||||
atomic.AddUint32(&out.Invalids, 1)
|
||||
}
|
||||
case "content":
|
||||
out.Values[i] = ec._Policy_content(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
out.Invalids++
|
||||
atomic.AddUint32(&out.Invalids, 1)
|
||||
}
|
||||
case "reviewDate":
|
||||
out.Values[i] = ec._Policy_reviewDate(ctx, field, obj)
|
||||
case "owner":
|
||||
field := field
|
||||
|
||||
innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) {
|
||||
res = ec._Policy_owner(ctx, field, obj)
|
||||
if res == graphql.Null {
|
||||
atomic.AddUint32(&fs.Invalids, 1)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
if field.Deferrable != nil {
|
||||
dfs, ok := deferred[field.Deferrable.Label]
|
||||
di := 0
|
||||
if ok {
|
||||
dfs.AddField(field)
|
||||
di = len(dfs.Values) - 1
|
||||
} else {
|
||||
dfs = graphql.NewFieldSet([]graphql.CollectedField{field})
|
||||
deferred[field.Deferrable.Label] = dfs
|
||||
}
|
||||
dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler {
|
||||
return innerFunc(ctx, dfs)
|
||||
})
|
||||
|
||||
// don't run the out.Concurrently() call below
|
||||
out.Values[i] = graphql.Null
|
||||
continue
|
||||
}
|
||||
|
||||
out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) })
|
||||
case "createdAt":
|
||||
out.Values[i] = ec._Policy_createdAt(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
out.Invalids++
|
||||
atomic.AddUint32(&out.Invalids, 1)
|
||||
}
|
||||
case "updatedAt":
|
||||
out.Values[i] = ec._Policy_updatedAt(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
out.Invalids++
|
||||
atomic.AddUint32(&out.Invalids, 1)
|
||||
}
|
||||
default:
|
||||
panic("unknown field " + strconv.Quote(field.Name))
|
||||
@@ -19276,6 +19396,10 @@ func (ec *executionContext) marshalNPageInfo2ᚖgithubᚗcomᚋgetproboᚋprobo
|
||||
return ec._PageInfo(ctx, sel, v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalNPeople2githubᚗcomᚋgetproboᚋproboᚋpkgᚋapiᚋconsoleᚋv1ᚋtypesᚐPeople(ctx context.Context, sel ast.SelectionSet, v types.People) graphql.Marshaler {
|
||||
return ec._People(ctx, sel, &v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalNPeople2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋapiᚋconsoleᚋv1ᚋtypesᚐPeople(ctx context.Context, sel ast.SelectionSet, v *types.People) graphql.Marshaler {
|
||||
if v == nil {
|
||||
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
|
||||
@@ -20333,6 +20457,22 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
func (ec *executionContext) unmarshalOID2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋgidᚐGID(ctx context.Context, v any) (*gid.GID, error) {
|
||||
if v == nil {
|
||||
return nil, nil
|
||||
}
|
||||
res, err := types.UnmarshalGIDScalar(v)
|
||||
return &res, graphql.ErrorOnPath(ctx, err)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalOID2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋgidᚐGID(ctx context.Context, sel ast.SelectionSet, v *gid.GID) graphql.Marshaler {
|
||||
if v == nil {
|
||||
return graphql.Null
|
||||
}
|
||||
res := types.MarshalGIDScalar(*v)
|
||||
return res
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalOInt2ᚖint(ctx context.Context, v any) (*int, error) {
|
||||
if v == nil {
|
||||
return nil, nil
|
||||
|
||||
@@ -108,6 +108,7 @@ type CreatePolicyInput struct {
|
||||
Content string `json:"content"`
|
||||
Status coredata.PolicyStatus `json:"status"`
|
||||
ReviewDate *time.Time `json:"reviewDate,omitempty"`
|
||||
OwnerID gid.GID `json:"ownerId"`
|
||||
}
|
||||
|
||||
type CreatePolicyPayload struct {
|
||||
@@ -322,6 +323,7 @@ type Policy struct {
|
||||
Status coredata.PolicyStatus `json:"status"`
|
||||
Content string `json:"content"`
|
||||
ReviewDate *time.Time `json:"reviewDate,omitempty"`
|
||||
Owner *People `json:"owner"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
@@ -434,6 +436,7 @@ type UpdatePolicyInput struct {
|
||||
Content *string `json:"content,omitempty"`
|
||||
Status *coredata.PolicyStatus `json:"status,omitempty"`
|
||||
ReviewDate *time.Time `json:"reviewDate,omitempty"`
|
||||
OwnerID *gid.GID `json:"ownerId,omitempty"`
|
||||
}
|
||||
|
||||
type UpdatePolicyPayload struct {
|
||||
|
||||
@@ -383,6 +383,7 @@ func (r *mutationResolver) CreatePolicy(ctx context.Context, input types.CreateP
|
||||
Content: input.Content,
|
||||
Status: input.Status,
|
||||
ReviewDate: input.ReviewDate,
|
||||
OwnerID: input.OwnerID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot create policy: %w", err)
|
||||
@@ -402,6 +403,7 @@ func (r *mutationResolver) UpdatePolicy(ctx context.Context, input types.UpdateP
|
||||
Content: input.Content,
|
||||
Status: input.Status,
|
||||
ReviewDate: input.ReviewDate,
|
||||
OwnerID: input.OwnerID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot update policy: %w", err)
|
||||
@@ -472,6 +474,22 @@ func (r *organizationResolver) Policies(ctx context.Context, obj *types.Organiza
|
||||
return types.NewPolicyConnection(page), nil
|
||||
}
|
||||
|
||||
// Owner is the resolver for the owner field.
|
||||
func (r *policyResolver) Owner(ctx context.Context, obj *types.Policy) (*types.People, error) {
|
||||
policy, err := r.proboSvc.Policies.Get(ctx, obj.ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot get policy: %w", err)
|
||||
}
|
||||
|
||||
// Get the owner
|
||||
owner, err := r.proboSvc.GetPeople(ctx, policy.OwnerID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot get owner: %w", err)
|
||||
}
|
||||
|
||||
return types.NewPeople(owner), nil
|
||||
}
|
||||
|
||||
// Node is the resolver for the node field.
|
||||
func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error) {
|
||||
switch id.EntityType() {
|
||||
@@ -613,6 +631,9 @@ func (r *Resolver) Mutation() schema.MutationResolver { return &mutationResolver
|
||||
// Organization returns schema.OrganizationResolver implementation.
|
||||
func (r *Resolver) Organization() schema.OrganizationResolver { return &organizationResolver{r} }
|
||||
|
||||
// Policy returns schema.PolicyResolver implementation.
|
||||
func (r *Resolver) Policy() schema.PolicyResolver { return &policyResolver{r} }
|
||||
|
||||
// Query returns schema.QueryResolver implementation.
|
||||
func (r *Resolver) Query() schema.QueryResolver { return &queryResolver{r} }
|
||||
|
||||
@@ -627,6 +648,7 @@ type evidenceResolver struct{ *Resolver }
|
||||
type frameworkResolver struct{ *Resolver }
|
||||
type mutationResolver struct{ *Resolver }
|
||||
type organizationResolver struct{ *Resolver }
|
||||
type policyResolver struct{ *Resolver }
|
||||
type queryResolver struct{ *Resolver }
|
||||
type taskResolver struct{ *Resolver }
|
||||
type userResolver struct{ *Resolver }
|
||||
|
||||
1
pkg/probo/coredata/migrations/20250305T111053Z.sql
Normal file
1
pkg/probo/coredata/migrations/20250305T111053Z.sql
Normal file
@@ -0,0 +1 @@
|
||||
ALTER TABLE policies ADD COLUMN owner_id TEXT REFERENCES peoples(id) NOT NULL;
|
||||
@@ -16,6 +16,7 @@ type (
|
||||
Policy struct {
|
||||
ID gid.GID `db:"id"`
|
||||
OrganizationID gid.GID `db:"organization_id"`
|
||||
OwnerID gid.GID `db:"owner_id"`
|
||||
Status PolicyStatus `db:"status"`
|
||||
Name string `db:"name"`
|
||||
Content string `db:"content"`
|
||||
@@ -33,6 +34,7 @@ type (
|
||||
Content *string
|
||||
Status *PolicyStatus
|
||||
ReviewDate **time.Time
|
||||
OwnerID *gid.GID
|
||||
}
|
||||
)
|
||||
|
||||
@@ -50,6 +52,7 @@ func (p *Policy) LoadByID(
|
||||
SELECT
|
||||
id,
|
||||
organization_id,
|
||||
owner_id,
|
||||
name,
|
||||
status,
|
||||
content,
|
||||
@@ -96,6 +99,7 @@ func (p *Policies) LoadByOrganizationID(
|
||||
SELECT
|
||||
id,
|
||||
organization_id,
|
||||
owner_id,
|
||||
name,
|
||||
status,
|
||||
content,
|
||||
@@ -141,6 +145,7 @@ INSERT INTO
|
||||
policies (
|
||||
id,
|
||||
organization_id,
|
||||
owner_id,
|
||||
name,
|
||||
status,
|
||||
content,
|
||||
@@ -152,6 +157,7 @@ INSERT INTO
|
||||
VALUES (
|
||||
@policy_id,
|
||||
@organization_id,
|
||||
@owner_id,
|
||||
@name,
|
||||
@status,
|
||||
@content,
|
||||
@@ -165,6 +171,7 @@ VALUES (
|
||||
args := pgx.StrictNamedArgs{
|
||||
"policy_id": p.ID,
|
||||
"organization_id": p.OrganizationID,
|
||||
"owner_id": p.OwnerID,
|
||||
"name": p.Name,
|
||||
"status": p.Status,
|
||||
"content": p.Content,
|
||||
@@ -207,6 +214,7 @@ UPDATE policies SET
|
||||
status = COALESCE(@status, status),
|
||||
content = COALESCE(@content, content),
|
||||
review_date = COALESCE(@review_date, review_date),
|
||||
owner_id = COALESCE(@owner_id, owner_id),
|
||||
updated_at = @updated_at,
|
||||
version = version + 1
|
||||
WHERE %s
|
||||
@@ -215,6 +223,7 @@ WHERE %s
|
||||
RETURNING
|
||||
id,
|
||||
organization_id,
|
||||
owner_id,
|
||||
name,
|
||||
content,
|
||||
review_date,
|
||||
@@ -243,6 +252,9 @@ RETURNING
|
||||
if params.ReviewDate != nil {
|
||||
args["review_date"] = *params.ReviewDate
|
||||
}
|
||||
if params.OwnerID != nil {
|
||||
args["owner_id"] = *params.OwnerID
|
||||
}
|
||||
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ type (
|
||||
Status coredata.PolicyStatus
|
||||
Content string
|
||||
ReviewDate *time.Time
|
||||
OwnerID gid.GID
|
||||
}
|
||||
|
||||
UpdatePolicyRequest struct {
|
||||
@@ -31,6 +32,7 @@ type (
|
||||
Content *string
|
||||
Status *coredata.PolicyStatus
|
||||
ReviewDate *time.Time
|
||||
OwnerID *gid.GID
|
||||
}
|
||||
)
|
||||
|
||||
@@ -68,6 +70,7 @@ func (s *PolicyService) Create(
|
||||
policy := &coredata.Policy{
|
||||
ID: policyID,
|
||||
OrganizationID: req.OrganizationID,
|
||||
OwnerID: req.OwnerID,
|
||||
Name: req.Name,
|
||||
Content: req.Content,
|
||||
Status: req.Status,
|
||||
@@ -108,6 +111,7 @@ func (s *PolicyService) Update(
|
||||
Content: req.Content,
|
||||
Status: req.Status,
|
||||
ReviewDate: &req.ReviewDate,
|
||||
OwnerID: req.OwnerID,
|
||||
}
|
||||
|
||||
policy := &coredata.Policy{ID: req.ID}
|
||||
|
||||
Reference in New Issue
Block a user