diff --git a/apps/console/src/components/PeopleSelector.tsx b/apps/console/src/components/PeopleSelector.tsx new file mode 100644 index 000000000..5f309f903 --- /dev/null +++ b/apps/console/src/components/PeopleSelector.tsx @@ -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(selectedPersonId || ""); + + useEffect(() => { + if (selectedPersonId) { + setValue(selectedPersonId); + } + }, [selectedPersonId]); + + const handleValueChange = (newValue: string) => { + setValue(newValue); + onSelect(newValue); + }; + + return ( + + ); +} diff --git a/apps/console/src/components/__generated__/PeopleSelector_organization.graphql.ts b/apps/console/src/components/__generated__/PeopleSelector_organization.graphql.ts new file mode 100644 index 000000000..64dd84c8c --- /dev/null +++ b/apps/console/src/components/__generated__/PeopleSelector_organization.graphql.ts @@ -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; diff --git a/apps/console/src/pages/CreatePolicyPage.tsx b/apps/console/src/pages/CreatePolicyPage.tsx index ea9e16b17..d957d429f 100644 --- a/apps/console/src/pages/CreatePolicyPage.tsx +++ b/apps/console/src/pages/CreatePolicyPage.tsx @@ -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; +}) { const navigate = useNavigate(); const { organizationId } = useParams(); + const data = usePreloadedQuery( + 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(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() { +
+ + +
+