Add create people

Signed-off-by: gearnode <bryan@frimin.fr>
This commit is contained in:
gearnode
2025-02-12 16:39:31 -08:00
parent 29258e65ff
commit 5864e092e6
15 changed files with 1126 additions and 170 deletions

View File

@@ -34,7 +34,8 @@
"tailwindcss-animate": "^1.0.7", "tailwindcss-animate": "^1.0.7",
"@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-label": "^2.0.2", "@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-radio-group": "^1.1.3" "@radix-ui/react-radio-group": "^1.1.3",
"@radix-ui/react-select": "^2.0.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.26.7", "@babel/core": "^7.26.7",

View File

@@ -29,6 +29,7 @@ const FrameworkListPage = lazy(() => import("./pages/FrameworkListPage"));
const FrameworkOverviewPage = lazy(() => import("./pages/FrameworkOverviewPage")); const FrameworkOverviewPage = lazy(() => import("./pages/FrameworkOverviewPage"));
const VendorOverviewPage = lazy(() => import("./pages/VendorOverviewPage")); const VendorOverviewPage = lazy(() => import("./pages/VendorOverviewPage"));
const SettingsPage = lazy(() => import("./pages/SettingsPage")); const SettingsPage = lazy(() => import("./pages/SettingsPage"));
const CreatePeoplePage = lazy(() => import("./pages/CreatePeoplePage"));
function App() { function App() {
return ( return (
@@ -67,6 +68,16 @@ function App() {
</Suspense> </Suspense>
} }
/> />
<Route
path="/peoples/create"
element={
<Suspense>
<ErrorBoundaryWithLocation>
<CreatePeoplePage />
</ErrorBoundaryWithLocation>
</Suspense>
}
/>
<Route <Route
path="/vendors" path="/vendors"
element={ element={

View File

@@ -0,0 +1,91 @@
import * as React from "react"
import * as SelectPrimitive from "@radix-ui/react-select"
import { Check, ChevronDown } from "lucide-react"
import { cn } from "@/lib/utils"
const Select = SelectPrimitive.Root
const SelectGroup = SelectPrimitive.Group
const SelectValue = SelectPrimitive.Value
const SelectTrigger = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
<SelectPrimitive.Trigger
ref={ref}
className={cn(
"flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
{...props}
>
{children}
<ChevronDown className="h-4 w-4 opacity-50" />
</SelectPrimitive.Trigger>
))
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
const SelectContent = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
>(({ className, children, position = "popper", ...props }, ref) => (
<SelectPrimitive.Portal>
<SelectPrimitive.Content
ref={ref}
className={cn(
"relative z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
position === "popper" &&
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
className
)}
position={position}
{...props}
>
<SelectPrimitive.Viewport
className={cn(
"p-1",
position === "popper" &&
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
)}
>
{children}
</SelectPrimitive.Viewport>
</SelectPrimitive.Content>
</SelectPrimitive.Portal>
))
SelectContent.displayName = SelectPrimitive.Content.displayName
const SelectItem = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
>(({ className, children, ...props }, ref) => (
<SelectPrimitive.Item
ref={ref}
className={cn(
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
)}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<SelectPrimitive.ItemIndicator>
<Check className="h-4 w-4" />
</SelectPrimitive.ItemIndicator>
</span>
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
</SelectPrimitive.Item>
))
SelectItem.displayName = SelectPrimitive.Item.displayName
export {
Select,
SelectGroup,
SelectValue,
SelectTrigger,
SelectContent,
SelectItem,
}

View File

@@ -0,0 +1,180 @@
import { Suspense, useEffect, useState } from "react";
import { useNavigate } from "react-router";
import {
graphql,
PreloadedQuery,
usePreloadedQuery,
useQueryLoader,
useMutation,
ConnectionHandler,
useRelayEnvironment,
} from "react-relay";
import { Helmet } from "react-helmet-async";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import type { CreatePeoplePageQuery as CreatePeoplePageQueryType } from "./__generated__/CreatePeoplePageQuery.graphql";
const createPeoplePageQuery = graphql`
query CreatePeoplePageQuery {
node(id: "AZSfP_xAcAC5IAAAAAAltA") {
id
... on Organization {
name
}
}
}
`;
const createPeopleMutation = graphql`
mutation CreatePeoplePageCreatePeopleMutation($input: CreatePeopleInput!) {
createPeople(input: $input) {
id
fullName
primaryEmailAddress
kind
}
}
`;
function CreatePeoplePageContent({
queryRef,
}: {
queryRef: PreloadedQuery<CreatePeoplePageQueryType>;
}) {
const navigate = useNavigate();
const environment = useRelayEnvironment();
const data = usePreloadedQuery(createPeoplePageQuery, queryRef);
const [createPeople, isCreatingPeople] = useMutation(createPeopleMutation);
const [kind, setKind] = useState<'EMPLOYEE' | 'CONTRACTOR' | 'VENDOR'>('EMPLOYEE');
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
createPeople({
variables: {
input: {
organizationId: data.node.id,
fullName: formData.get('fullName') as string,
primaryEmailAddress: formData.get('primaryEmailAddress') as string,
kind,
},
},
onCompleted() {
// Invalidate the peoples list query
environment.commitUpdate((store) => {
const organization = store.get(data.node.id);
if (organization) {
organization.invalidateRecord();
}
});
navigate('/peoples');
},
});
};
return (
<div className="p-6 max-w-2xl mx-auto">
<Helmet>
<title>Create People - Probo Console</title>
</Helmet>
<div className="space-y-6">
<div>
<h1 className="text-2xl font-semibold tracking-tight">Create People</h1>
<p className="text-sm text-muted-foreground">
Add a new person to your organization.
</p>
</div>
<Card>
<CardHeader>
<CardTitle>People Information</CardTitle>
</CardHeader>
<CardContent>
<form onSubmit={handleSubmit} className="space-y-4">
<div className="space-y-2">
<Label htmlFor="kind">Kind</Label>
<Select
value={kind}
onValueChange={(value) => setKind(value as typeof kind)}
>
<SelectTrigger>
<SelectValue placeholder="Select a kind" />
</SelectTrigger>
<SelectContent>
<SelectItem value="EMPLOYEE">Employee</SelectItem>
<SelectItem value="CONTRACTOR">Contractor</SelectItem>
<SelectItem value="VENDOR">Vendor</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label htmlFor="fullName">Full Name</Label>
<Input
id="fullName"
name="fullName"
placeholder="John Doe"
required
/>
</div>
<div className="space-y-2">
<Label htmlFor="primaryEmailAddress">Email Address</Label>
<Input
id="primaryEmailAddress"
name="primaryEmailAddress"
type="email"
placeholder="john@example.com"
required
/>
</div>
<div className="flex justify-end gap-4">
<Button
type="button"
variant="outline"
onClick={() => navigate('/peoples')}
>
Cancel
</Button>
<Button type="submit" disabled={isCreatingPeople}>
{isCreatingPeople ? "Creating..." : "Create People"}
</Button>
</div>
</form>
</CardContent>
</Card>
</div>
</div>
);
}
export default function CreatePeoplePage() {
const [queryRef, loadQuery] = useQueryLoader<CreatePeoplePageQueryType>(createPeoplePageQuery);
useEffect(() => {
loadQuery({});
}, [loadQuery]);
if (!queryRef) {
return null;
}
return (
<Suspense fallback={null}>
<CreatePeoplePageContent queryRef={queryRef} />
</Suspense>
);
}

View File

@@ -11,6 +11,7 @@ import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { CircleUser, Globe, Shield } from "lucide-react"; import { CircleUser, Globe, Shield } from "lucide-react";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Link } from "react-router";
import { Helmet } from "react-helmet-async"; import { Helmet } from "react-helmet-async";
import type { PeopleListPageQuery as PeopleListPageQueryType } from "./__generated__/PeopleListPageQuery.graphql"; import type { PeopleListPageQuery as PeopleListPageQueryType } from "./__generated__/PeopleListPageQuery.graphql";
@@ -21,13 +22,15 @@ const PeopleListPageQuery = graphql`
node(id: "AZSfP_xAcAC5IAAAAAAltA") { node(id: "AZSfP_xAcAC5IAAAAAAltA") {
id id
... on Organization { ... on Organization {
peoples(first: $first, after: $after, last: $last, before: $before) { peoples(first: $first, after: $after, last: $last, before: $before)
@connection(key: "PeopleListPageQuery_peoples") {
edges { edges {
node { node {
id id
fullName fullName
primaryEmailAddress primaryEmailAddress
additionalEmailAddresses additionalEmailAddresses
kind
createdAt createdAt
updatedAt updatedAt
} }
@@ -85,10 +88,19 @@ function PeopleListPageContent({
return ( return (
<div className="p-6 space-y-6"> <div className="p-6 space-y-6">
<div className="space-y-1"> <div className="space-y-1">
<h2 className="text-2xl font-semibold tracking-tight">Peoples</h2> <div className="flex items-center justify-between">
<p className="text-sm text-muted-foreground"> <div>
Keep track of your company's workforce and their progress towards completing tasks assigned to them. <h2 className="text-2xl font-semibold tracking-tight">People</h2>
</p> <p className="text-sm text-muted-foreground">
Manage your organization's people.
</p>
</div>
<Button asChild>
<Link to="/peoples/create">
Create People
</Link>
</Button>
</div>
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
{peoples.map((person) => ( {peoples.map((person) => (
@@ -107,7 +119,8 @@ function PeopleListPageContent({
</div> </div>
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<Badge variant="secondary" className="font-medium"> <Badge variant="secondary" className="font-medium">
Admin {person?.kind === 'EMPLOYEE' ? 'Employee' :
person?.kind === 'CONTRACTOR' ? 'Contractor' : 'Vendor'}
</Badge> </Badge>
<div className="flex gap-1"> <div className="flex gap-1">
<CircleUser className="h-4 w-4 text-muted-foreground" /> <CircleUser className="h-4 w-4 text-muted-foreground" />

View File

@@ -0,0 +1,120 @@
/**
* @generated SignedSource<<c8acfac5384ba48fe05ca8f73f32a8c5>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type PeopleKind = "CONTRACTOR" | "EMPLOYEE" | "VENDOR";
export type CreatePeopleInput = {
fullName: string;
kind: PeopleKind;
organizationId: string;
primaryEmailAddress: string;
};
export type CreatePeoplePageCreatePeopleMutation$variables = {
input: CreatePeopleInput;
};
export type CreatePeoplePageCreatePeopleMutation$data = {
readonly createPeople: {
readonly fullName: string;
readonly id: string;
readonly kind: PeopleKind;
readonly primaryEmailAddress: string;
};
};
export type CreatePeoplePageCreatePeopleMutation = {
response: CreatePeoplePageCreatePeopleMutation$data;
variables: CreatePeoplePageCreatePeopleMutation$variables;
};
const node: ConcreteRequest = (function(){
var v0 = [
{
"defaultValue": null,
"kind": "LocalArgument",
"name": "input"
}
],
v1 = [
{
"alias": null,
"args": [
{
"kind": "Variable",
"name": "input",
"variableName": "input"
}
],
"concreteType": "People",
"kind": "LinkedField",
"name": "createPeople",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "id",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "fullName",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "primaryEmailAddress",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "kind",
"storageKey": null
}
],
"storageKey": null
}
];
return {
"fragment": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Fragment",
"metadata": null,
"name": "CreatePeoplePageCreatePeopleMutation",
"selections": (v1/*: any*/),
"type": "Mutation",
"abstractKey": null
},
"kind": "Request",
"operation": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Operation",
"name": "CreatePeoplePageCreatePeopleMutation",
"selections": (v1/*: any*/)
},
"params": {
"cacheID": "6438f79d9a2e3d3e5cfcc98c4107e512",
"id": null,
"metadata": {},
"name": "CreatePeoplePageCreatePeopleMutation",
"operationKind": "mutation",
"text": "mutation CreatePeoplePageCreatePeopleMutation(\n $input: CreatePeopleInput!\n) {\n createPeople(input: $input) {\n id\n fullName\n primaryEmailAddress\n kind\n }\n}\n"
}
};
})();
(node as any).hash = "55f9379fac4d33ca3b2c21acf80094de";
export default node;

View File

@@ -0,0 +1,118 @@
/**
* @generated SignedSource<<1440fead35c1cf239edc5574dcdf9dc3>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type CreatePeoplePageQuery$variables = Record<PropertyKey, never>;
export type CreatePeoplePageQuery$data = {
readonly node: {
readonly id: string;
readonly name?: string;
};
};
export type CreatePeoplePageQuery = {
response: CreatePeoplePageQuery$data;
variables: CreatePeoplePageQuery$variables;
};
const node: ConcreteRequest = (function(){
var v0 = [
{
"kind": "Literal",
"name": "id",
"value": "AZSfP_xAcAC5IAAAAAAltA"
}
],
v1 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "id",
"storageKey": null
},
v2 = {
"kind": "InlineFragment",
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "name",
"storageKey": null
}
],
"type": "Organization",
"abstractKey": null
};
return {
"fragment": {
"argumentDefinitions": [],
"kind": "Fragment",
"metadata": null,
"name": "CreatePeoplePageQuery",
"selections": [
{
"alias": null,
"args": (v0/*: any*/),
"concreteType": null,
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v1/*: any*/),
(v2/*: any*/)
],
"storageKey": "node(id:\"AZSfP_xAcAC5IAAAAAAltA\")"
}
],
"type": "Query",
"abstractKey": null
},
"kind": "Request",
"operation": {
"argumentDefinitions": [],
"kind": "Operation",
"name": "CreatePeoplePageQuery",
"selections": [
{
"alias": null,
"args": (v0/*: any*/),
"concreteType": null,
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "__typename",
"storageKey": null
},
(v1/*: any*/),
(v2/*: any*/)
],
"storageKey": "node(id:\"AZSfP_xAcAC5IAAAAAAltA\")"
}
]
},
"params": {
"cacheID": "e3812048d135626b79aa289d77ecd630",
"id": null,
"metadata": {},
"name": "CreatePeoplePageQuery",
"operationKind": "query",
"text": "query CreatePeoplePageQuery {\n node(id: \"AZSfP_xAcAC5IAAAAAAltA\") {\n __typename\n id\n ... on Organization {\n name\n }\n }\n}\n"
}
};
})();
(node as any).hash = "629fdb563382b22be4a6c1f17fafda96";
export default node;

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<2b0f0d83fcebe25d4dfe1209c757c792>> * @generated SignedSource<<c0df862d094bf6825bbda32ecda36bdd>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -9,6 +9,7 @@
// @ts-nocheck // @ts-nocheck
import { ConcreteRequest } from 'relay-runtime'; import { ConcreteRequest } from 'relay-runtime';
export type PeopleKind = "CONTRACTOR" | "EMPLOYEE" | "VENDOR";
export type PeopleListPageQuery$variables = { export type PeopleListPageQuery$variables = {
after?: any | null | undefined; after?: any | null | undefined;
before?: any | null | undefined; before?: any | null | undefined;
@@ -26,6 +27,7 @@ export type PeopleListPageQuery$data = {
readonly createdAt: any; readonly createdAt: any;
readonly fullName: string; readonly fullName: string;
readonly id: string; readonly id: string;
readonly kind: PeopleKind;
readonly primaryEmailAddress: string; readonly primaryEmailAddress: string;
readonly updatedAt: any; readonly updatedAt: any;
}; };
@@ -80,148 +82,148 @@ v5 = {
"storageKey": null "storageKey": null
}, },
v6 = { v6 = {
"kind": "InlineFragment", "alias": null,
"selections": [ "args": null,
{ "kind": "ScalarField",
"alias": null, "name": "__typename",
"args": [ "storageKey": null
{ },
"kind": "Variable", v7 = [
"name": "after", {
"variableName": "after" "alias": null,
}, "args": null,
{ "concreteType": "PeopleEdge",
"kind": "Variable", "kind": "LinkedField",
"name": "before", "name": "edges",
"variableName": "before" "plural": true,
}, "selections": [
{ {
"kind": "Variable", "alias": null,
"name": "first", "args": null,
"variableName": "first" "concreteType": "People",
}, "kind": "LinkedField",
{ "name": "node",
"kind": "Variable", "plural": false,
"name": "last", "selections": [
"variableName": "last" (v5/*: any*/),
} {
], "alias": null,
"concreteType": "PeopleConnection", "args": null,
"kind": "LinkedField", "kind": "ScalarField",
"name": "peoples", "name": "fullName",
"plural": false, "storageKey": null
"selections": [ },
{ {
"alias": null, "alias": null,
"args": null, "args": null,
"concreteType": "PeopleEdge", "kind": "ScalarField",
"kind": "LinkedField", "name": "primaryEmailAddress",
"name": "edges", "storageKey": null
"plural": true, },
"selections": [ {
{ "alias": null,
"alias": null, "args": null,
"args": null, "kind": "ScalarField",
"concreteType": "People", "name": "additionalEmailAddresses",
"kind": "LinkedField", "storageKey": null
"name": "node", },
"plural": false, {
"selections": [ "alias": null,
(v5/*: any*/), "args": null,
{ "kind": "ScalarField",
"alias": null, "name": "kind",
"args": null, "storageKey": null
"kind": "ScalarField", },
"name": "fullName", {
"storageKey": null "alias": null,
}, "args": null,
{ "kind": "ScalarField",
"alias": null, "name": "createdAt",
"args": null, "storageKey": null
"kind": "ScalarField", },
"name": "primaryEmailAddress", {
"storageKey": null "alias": null,
}, "args": null,
{ "kind": "ScalarField",
"alias": null, "name": "updatedAt",
"args": null, "storageKey": null
"kind": "ScalarField", },
"name": "additionalEmailAddresses", (v6/*: any*/)
"storageKey": null ],
}, "storageKey": null
{ },
"alias": null, {
"args": null, "alias": null,
"kind": "ScalarField", "args": null,
"name": "createdAt", "kind": "ScalarField",
"storageKey": null "name": "cursor",
}, "storageKey": null
{ }
"alias": null, ],
"args": null, "storageKey": null
"kind": "ScalarField", },
"name": "updatedAt", {
"storageKey": null "alias": null,
} "args": null,
], "concreteType": "PageInfo",
"storageKey": null "kind": "LinkedField",
}, "name": "pageInfo",
{ "plural": false,
"alias": null, "selections": [
"args": null, {
"kind": "ScalarField", "alias": null,
"name": "cursor", "args": null,
"storageKey": null "kind": "ScalarField",
} "name": "hasNextPage",
], "storageKey": null
"storageKey": null },
}, {
{ "alias": null,
"alias": null, "args": null,
"args": null, "kind": "ScalarField",
"concreteType": "PageInfo", "name": "hasPreviousPage",
"kind": "LinkedField", "storageKey": null
"name": "pageInfo", },
"plural": false, {
"selections": [ "alias": null,
{ "args": null,
"alias": null, "kind": "ScalarField",
"args": null, "name": "startCursor",
"kind": "ScalarField", "storageKey": null
"name": "hasNextPage", },
"storageKey": null {
}, "alias": null,
{ "args": null,
"alias": null, "kind": "ScalarField",
"args": null, "name": "endCursor",
"kind": "ScalarField", "storageKey": null
"name": "hasPreviousPage", }
"storageKey": null ],
}, "storageKey": null
{ }
"alias": null, ],
"args": null, v8 = [
"kind": "ScalarField", {
"name": "startCursor", "kind": "Variable",
"storageKey": null "name": "after",
}, "variableName": "after"
{ },
"alias": null, {
"args": null, "kind": "Variable",
"kind": "ScalarField", "name": "before",
"name": "endCursor", "variableName": "before"
"storageKey": null },
} {
], "kind": "Variable",
"storageKey": null "name": "first",
} "variableName": "first"
], },
"storageKey": null {
} "kind": "Variable",
], "name": "last",
"type": "Organization", "variableName": "last"
"abstractKey": null }
}; ];
return { return {
"fragment": { "fragment": {
"argumentDefinitions": [ "argumentDefinitions": [
@@ -243,7 +245,23 @@ return {
"plural": false, "plural": false,
"selections": [ "selections": [
(v5/*: any*/), (v5/*: any*/),
(v6/*: any*/) {
"kind": "InlineFragment",
"selections": [
{
"alias": "peoples",
"args": null,
"concreteType": "PeopleConnection",
"kind": "LinkedField",
"name": "__PeopleListPageQuery_peoples_connection",
"plural": false,
"selections": (v7/*: any*/),
"storageKey": null
}
],
"type": "Organization",
"abstractKey": null
}
], ],
"storageKey": "node(id:\"AZSfP_xAcAC5IAAAAAAltA\")" "storageKey": "node(id:\"AZSfP_xAcAC5IAAAAAAltA\")"
} }
@@ -270,31 +288,62 @@ return {
"name": "node", "name": "node",
"plural": false, "plural": false,
"selections": [ "selections": [
{ (v6/*: any*/),
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "__typename",
"storageKey": null
},
(v5/*: any*/), (v5/*: any*/),
(v6/*: any*/) {
"kind": "InlineFragment",
"selections": [
{
"alias": null,
"args": (v8/*: any*/),
"concreteType": "PeopleConnection",
"kind": "LinkedField",
"name": "peoples",
"plural": false,
"selections": (v7/*: any*/),
"storageKey": null
},
{
"alias": null,
"args": (v8/*: any*/),
"filters": null,
"handle": "connection",
"key": "PeopleListPageQuery_peoples",
"kind": "LinkedHandle",
"name": "peoples"
}
],
"type": "Organization",
"abstractKey": null
}
], ],
"storageKey": "node(id:\"AZSfP_xAcAC5IAAAAAAltA\")" "storageKey": "node(id:\"AZSfP_xAcAC5IAAAAAAltA\")"
} }
] ]
}, },
"params": { "params": {
"cacheID": "0074149682b4e4a31aa559fe8431d567", "cacheID": "480538011d1eccb9405f2d495417fc8d",
"id": null, "id": null,
"metadata": {}, "metadata": {
"connection": [
{
"count": null,
"cursor": null,
"direction": "bidirectional",
"path": [
"node",
"peoples"
]
}
]
},
"name": "PeopleListPageQuery", "name": "PeopleListPageQuery",
"operationKind": "query", "operationKind": "query",
"text": "query PeopleListPageQuery(\n $first: Int\n $after: CursorKey\n $last: Int\n $before: CursorKey\n) {\n node(id: \"AZSfP_xAcAC5IAAAAAAltA\") {\n __typename\n id\n ... on Organization {\n peoples(first: $first, after: $after, last: $last, before: $before) {\n edges {\n node {\n id\n fullName\n primaryEmailAddress\n additionalEmailAddresses\n createdAt\n updatedAt\n }\n cursor\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n}\n" "text": "query PeopleListPageQuery(\n $first: Int\n $after: CursorKey\n $last: Int\n $before: CursorKey\n) {\n node(id: \"AZSfP_xAcAC5IAAAAAAltA\") {\n __typename\n id\n ... on Organization {\n peoples(first: $first, after: $after, last: $last, before: $before) {\n edges {\n node {\n id\n fullName\n primaryEmailAddress\n additionalEmailAddresses\n kind\n createdAt\n updatedAt\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n}\n"
} }
}; };
})(); })();
(node as any).hash = "5ca962add045876503cb7290d20b9f8b"; (node as any).hash = "cc8b466dc73007c5590ba2e8ffc6def3";
export default node; export default node;

2
go.mod
View File

@@ -7,6 +7,7 @@ require (
github.com/99designs/gqlgen v0.17.63 github.com/99designs/gqlgen v0.17.63
github.com/go-chi/chi/v5 v5.2.0 github.com/go-chi/chi/v5 v5.2.0
github.com/go-chi/cors v1.2.1 github.com/go-chi/cors v1.2.1
github.com/google/uuid v1.6.0
github.com/jackc/pgx/v5 v5.7.2 github.com/jackc/pgx/v5 v5.7.2
github.com/prometheus/client_golang v1.20.5 github.com/prometheus/client_golang v1.20.5
github.com/vektah/gqlparser/v2 v2.5.21 github.com/vektah/gqlparser/v2 v2.5.21
@@ -25,7 +26,6 @@ require (
github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/websocket v1.5.3 // indirect github.com/gorilla/websocket v1.5.3 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.0 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect

50
package-lock.json generated
View File

@@ -26,6 +26,7 @@
"@radix-ui/react-label": "^2.0.2", "@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-progress": "^1.1.2", "@radix-ui/react-progress": "^1.1.2",
"@radix-ui/react-radio-group": "^1.1.3", "@radix-ui/react-radio-group": "^1.1.3",
"@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-separator": "^1.1.2", "@radix-ui/react-separator": "^1.1.2",
"@radix-ui/react-slot": "^1.1.2", "@radix-ui/react-slot": "^1.1.2",
"@radix-ui/react-tooltip": "^1.1.8", "@radix-ui/react-tooltip": "^1.1.8",
@@ -2255,6 +2256,12 @@
"resolved": "packages/tsconfig", "resolved": "packages/tsconfig",
"link": true "link": true
}, },
"node_modules/@radix-ui/number": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.0.tgz",
"integrity": "sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==",
"license": "MIT"
},
"node_modules/@radix-ui/primitive": { "node_modules/@radix-ui/primitive": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz",
@@ -2844,6 +2851,49 @@
} }
} }
}, },
"node_modules/@radix-ui/react-select": {
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.1.6.tgz",
"integrity": "sha512-T6ajELxRvTuAMWH0YmRJ1qez+x4/7Nq7QIx7zJ0VK3qaEWdnWpNbEDnmWldG1zBDwqrLy5aLMUWcoGirVj5kMg==",
"license": "MIT",
"dependencies": {
"@radix-ui/number": "1.1.0",
"@radix-ui/primitive": "1.1.1",
"@radix-ui/react-collection": "1.1.2",
"@radix-ui/react-compose-refs": "1.1.1",
"@radix-ui/react-context": "1.1.1",
"@radix-ui/react-direction": "1.1.0",
"@radix-ui/react-dismissable-layer": "1.1.5",
"@radix-ui/react-focus-guards": "1.1.1",
"@radix-ui/react-focus-scope": "1.1.2",
"@radix-ui/react-id": "1.1.0",
"@radix-ui/react-popper": "1.2.2",
"@radix-ui/react-portal": "1.1.4",
"@radix-ui/react-primitive": "2.0.2",
"@radix-ui/react-slot": "1.1.2",
"@radix-ui/react-use-callback-ref": "1.1.0",
"@radix-ui/react-use-controllable-state": "1.1.0",
"@radix-ui/react-use-layout-effect": "1.1.0",
"@radix-ui/react-use-previous": "1.1.0",
"@radix-ui/react-visually-hidden": "1.1.2",
"aria-hidden": "^1.2.4",
"react-remove-scroll": "^2.6.3"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-separator": { "node_modules/@radix-ui/react-separator": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.2.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.2.tgz",

View File

@@ -31,6 +31,12 @@ enum EvidenceState {
EXPIRED EXPIRED
} }
enum PeopleKind {
EMPLOYEE
CONTRACTOR
VENDOR
}
type PageInfo { type PageInfo {
hasNextPage: Boolean! hasNextPage: Boolean!
hasPreviousPage: Boolean! hasPreviousPage: Boolean!
@@ -83,6 +89,7 @@ type People implements Node {
fullName: String! fullName: String!
primaryEmailAddress: String! primaryEmailAddress: String!
additionalEmailAddresses: [String!]! additionalEmailAddresses: [String!]!
kind: PeopleKind!
createdAt: Datetime! createdAt: Datetime!
updatedAt: Datetime! updatedAt: Datetime!
} }
@@ -291,6 +298,7 @@ type Mutation {
createVendor(input: CreateVendorInput!): Vendor! createVendor(input: CreateVendorInput!): Vendor!
deleteVendor(input: DeleteVendorInput!): Void! deleteVendor(input: DeleteVendorInput!): Void!
deletePeople(input: DeletePeopleInput!): Void! deletePeople(input: DeletePeopleInput!): Void!
createPeople(input: CreatePeopleInput!): People!
} }
input CreateVendorInput { input CreateVendorInput {
@@ -305,3 +313,10 @@ input DeleteVendorInput {
input DeletePeopleInput { input DeletePeopleInput {
peopleId: ID! peopleId: ID!
} }
input CreatePeopleInput {
organizationId: ID!
fullName: String!
primaryEmailAddress: String!
kind: PeopleKind!
}

View File

@@ -154,6 +154,7 @@ type ComplexityRoot struct {
} }
Mutation struct { Mutation struct {
CreatePeople func(childComplexity int, input types.CreatePeopleInput) int
CreateVendor func(childComplexity int, input types.CreateVendorInput) int CreateVendor func(childComplexity int, input types.CreateVendorInput) int
DeletePeople func(childComplexity int, input types.DeletePeopleInput) int DeletePeople func(childComplexity int, input types.DeletePeopleInput) int
DeleteVendor func(childComplexity int, input types.DeleteVendorInput) int DeleteVendor func(childComplexity int, input types.DeleteVendorInput) int
@@ -182,6 +183,7 @@ type ComplexityRoot struct {
CreatedAt func(childComplexity int) int CreatedAt func(childComplexity int) int
FullName func(childComplexity int) int FullName func(childComplexity int) int
ID func(childComplexity int) int ID func(childComplexity int) int
Kind func(childComplexity int) int
PrimaryEmailAddress func(childComplexity int) int PrimaryEmailAddress func(childComplexity int) int
UpdatedAt func(childComplexity int) int UpdatedAt func(childComplexity int) int
} }
@@ -272,6 +274,7 @@ type MutationResolver interface {
CreateVendor(ctx context.Context, input types.CreateVendorInput) (*types.Vendor, error) CreateVendor(ctx context.Context, input types.CreateVendorInput) (*types.Vendor, error)
DeleteVendor(ctx context.Context, input types.DeleteVendorInput) (string, error) DeleteVendor(ctx context.Context, input types.DeleteVendorInput) (string, error)
DeletePeople(ctx context.Context, input types.DeletePeopleInput) (string, error) DeletePeople(ctx context.Context, input types.DeletePeopleInput) (string, error)
CreatePeople(ctx context.Context, input types.CreatePeopleInput) (*types.People, error)
} }
type OrganizationResolver interface { type OrganizationResolver interface {
Frameworks(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.FrameworkConnection, error) Frameworks(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.FrameworkConnection, error)
@@ -703,6 +706,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.FrameworkEdge.Node(childComplexity), true return e.complexity.FrameworkEdge.Node(childComplexity), true
case "Mutation.createPeople":
if e.complexity.Mutation.CreatePeople == nil {
break
}
args, err := ec.field_Mutation_createPeople_args(context.TODO(), rawArgs)
if err != nil {
return 0, false
}
return e.complexity.Mutation.CreatePeople(childComplexity, args["input"].(types.CreatePeopleInput)), true
case "Mutation.createVendor": case "Mutation.createVendor":
if e.complexity.Mutation.CreateVendor == nil { if e.complexity.Mutation.CreateVendor == nil {
break break
@@ -866,6 +881,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.People.ID(childComplexity), true return e.complexity.People.ID(childComplexity), true
case "People.kind":
if e.complexity.People.Kind == nil {
break
}
return e.complexity.People.Kind(childComplexity), true
case "People.primaryEmailAddress": case "People.primaryEmailAddress":
if e.complexity.People.PrimaryEmailAddress == nil { if e.complexity.People.PrimaryEmailAddress == nil {
break break
@@ -1148,6 +1170,7 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler {
opCtx := graphql.GetOperationContext(ctx) opCtx := graphql.GetOperationContext(ctx)
ec := executionContext{opCtx, e, 0, 0, make(chan graphql.DeferredResult)} ec := executionContext{opCtx, e, 0, 0, make(chan graphql.DeferredResult)}
inputUnmarshalMap := graphql.BuildUnmarshalerMap( inputUnmarshalMap := graphql.BuildUnmarshalerMap(
ec.unmarshalInputCreatePeopleInput,
ec.unmarshalInputCreateVendorInput, ec.unmarshalInputCreateVendorInput,
ec.unmarshalInputDeletePeopleInput, ec.unmarshalInputDeletePeopleInput,
ec.unmarshalInputDeleteVendorInput, ec.unmarshalInputDeleteVendorInput,
@@ -1281,6 +1304,12 @@ enum EvidenceState {
EXPIRED EXPIRED
} }
enum PeopleKind {
EMPLOYEE
CONTRACTOR
VENDOR
}
type PageInfo { type PageInfo {
hasNextPage: Boolean! hasNextPage: Boolean!
hasPreviousPage: Boolean! hasPreviousPage: Boolean!
@@ -1333,6 +1362,7 @@ type People implements Node {
fullName: String! fullName: String!
primaryEmailAddress: String! primaryEmailAddress: String!
additionalEmailAddresses: [String!]! additionalEmailAddresses: [String!]!
kind: PeopleKind!
createdAt: Datetime! createdAt: Datetime!
updatedAt: Datetime! updatedAt: Datetime!
} }
@@ -1541,6 +1571,7 @@ type Mutation {
createVendor(input: CreateVendorInput!): Vendor! createVendor(input: CreateVendorInput!): Vendor!
deleteVendor(input: DeleteVendorInput!): Void! deleteVendor(input: DeleteVendorInput!): Void!
deletePeople(input: DeletePeopleInput!): Void! deletePeople(input: DeletePeopleInput!): Void!
createPeople(input: CreatePeopleInput!): People!
} }
input CreateVendorInput { input CreateVendorInput {
@@ -1554,6 +1585,13 @@ input DeleteVendorInput {
input DeletePeopleInput { input DeletePeopleInput {
peopleId: ID! peopleId: ID!
}
input CreatePeopleInput {
organizationId: ID!
fullName: String!
primaryEmailAddress: String!
kind: PeopleKind!
}`, BuiltIn: false}, }`, BuiltIn: false},
} }
var parsedSchema = gqlparser.MustLoadSchema(sources...) var parsedSchema = gqlparser.MustLoadSchema(sources...)
@@ -1870,6 +1908,29 @@ func (ec *executionContext) field_Framework_controls_argsBefore(
return zeroVal, nil return zeroVal, nil
} }
func (ec *executionContext) field_Mutation_createPeople_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
var err error
args := map[string]any{}
arg0, err := ec.field_Mutation_createPeople_argsInput(ctx, rawArgs)
if err != nil {
return nil, err
}
args["input"] = arg0
return args, nil
}
func (ec *executionContext) field_Mutation_createPeople_argsInput(
ctx context.Context,
rawArgs map[string]any,
) (types.CreatePeopleInput, error) {
ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("input"))
if tmp, ok := rawArgs["input"]; ok {
return ec.unmarshalNCreatePeopleInput2githubᚗcomᚋgetproboᚋproboᚋpkgᚋapiᚋconsoleᚋv1ᚋtypesᚐCreatePeopleInput(ctx, tmp)
}
var zeroVal types.CreatePeopleInput
return zeroVal, nil
}
func (ec *executionContext) field_Mutation_createVendor_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { func (ec *executionContext) field_Mutation_createVendor_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
var err error var err error
args := map[string]any{} args := map[string]any{}
@@ -4805,6 +4866,65 @@ func (ec *executionContext) fieldContext_Mutation_deletePeople(ctx context.Conte
return fc, nil return fc, nil
} }
func (ec *executionContext) _Mutation_createPeople(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Mutation_createPeople(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.Mutation().CreatePeople(rctx, fc.Args["input"].(types.CreatePeopleInput))
})
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_Mutation_createPeople(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "Mutation",
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)
}
return nil, fmt.Errorf("no field named %q was found under type People", field.Name)
},
}
ctx = graphql.WithFieldContext(ctx, fc)
if fc.Args, err = ec.field_Mutation_createPeople_args(ctx, field.ArgumentMap(ec.Variables)); err != nil {
ec.Error(ctx, err)
return fc, err
}
return fc, nil
}
func (ec *executionContext) _Organization_id(ctx context.Context, field graphql.CollectedField, obj *types.Organization) (ret graphql.Marshaler) { func (ec *executionContext) _Organization_id(ctx context.Context, field graphql.CollectedField, obj *types.Organization) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Organization_id(ctx, field) fc, err := ec.fieldContext_Organization_id(ctx, field)
if err != nil { if err != nil {
@@ -5440,6 +5560,44 @@ func (ec *executionContext) fieldContext_People_additionalEmailAddresses(_ conte
return fc, nil return fc, nil
} }
func (ec *executionContext) _People_kind(ctx context.Context, field graphql.CollectedField, obj *types.People) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_People_kind(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 obj.Kind, nil
})
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.PeopleKind)
fc.Result = res
return ec.marshalNPeopleKind2githubᚗcomᚋgetproboᚋproboᚋpkgᚋapiᚋconsoleᚋv1ᚋtypesᚐPeopleKind(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_People_kind(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "People",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type PeopleKind does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _People_createdAt(ctx context.Context, field graphql.CollectedField, obj *types.People) (ret graphql.Marshaler) { func (ec *executionContext) _People_createdAt(ctx context.Context, field graphql.CollectedField, obj *types.People) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_People_createdAt(ctx, field) fc, err := ec.fieldContext_People_createdAt(ctx, field)
if err != nil { if err != nil {
@@ -5687,6 +5845,8 @@ func (ec *executionContext) fieldContext_PeopleEdge_node(_ context.Context, fiel
return ec.fieldContext_People_primaryEmailAddress(ctx, field) return ec.fieldContext_People_primaryEmailAddress(ctx, field)
case "additionalEmailAddresses": case "additionalEmailAddresses":
return ec.fieldContext_People_additionalEmailAddresses(ctx, field) return ec.fieldContext_People_additionalEmailAddresses(ctx, field)
case "kind":
return ec.fieldContext_People_kind(ctx, field)
case "createdAt": case "createdAt":
return ec.fieldContext_People_createdAt(ctx, field) return ec.fieldContext_People_createdAt(ctx, field)
case "updatedAt": case "updatedAt":
@@ -8649,6 +8809,54 @@ func (ec *executionContext) fieldContext___Type_specifiedByURL(_ context.Context
// region **************************** input.gotpl ***************************** // region **************************** input.gotpl *****************************
func (ec *executionContext) unmarshalInputCreatePeopleInput(ctx context.Context, obj any) (types.CreatePeopleInput, error) {
var it types.CreatePeopleInput
asMap := map[string]any{}
for k, v := range obj.(map[string]any) {
asMap[k] = v
}
fieldsInOrder := [...]string{"organizationId", "fullName", "primaryEmailAddress", "kind"}
for _, k := range fieldsInOrder {
v, ok := asMap[k]
if !ok {
continue
}
switch k {
case "organizationId":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("organizationId"))
data, err := ec.unmarshalNID2githubᚗcomᚋgetproboᚋproboᚋpkgᚋgidᚐGID(ctx, v)
if err != nil {
return it, err
}
it.OrganizationID = data
case "fullName":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("fullName"))
data, err := ec.unmarshalNString2string(ctx, v)
if err != nil {
return it, err
}
it.FullName = data
case "primaryEmailAddress":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("primaryEmailAddress"))
data, err := ec.unmarshalNString2string(ctx, v)
if err != nil {
return it, err
}
it.PrimaryEmailAddress = data
case "kind":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("kind"))
data, err := ec.unmarshalNPeopleKind2githubᚗcomᚋgetproboᚋproboᚋpkgᚋapiᚋconsoleᚋv1ᚋtypesᚐPeopleKind(ctx, v)
if err != nil {
return it, err
}
it.Kind = data
}
}
return it, nil
}
func (ec *executionContext) unmarshalInputCreateVendorInput(ctx context.Context, obj any) (types.CreateVendorInput, error) { func (ec *executionContext) unmarshalInputCreateVendorInput(ctx context.Context, obj any) (types.CreateVendorInput, error) {
var it types.CreateVendorInput var it types.CreateVendorInput
asMap := map[string]any{} asMap := map[string]any{}
@@ -9715,6 +9923,13 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
out.Invalids++ out.Invalids++
} }
case "createPeople":
out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) {
return ec._Mutation_createPeople(ctx, field)
})
if out.Values[i] == graphql.Null {
out.Invalids++
}
default: default:
panic("unknown field " + strconv.Quote(field.Name)) panic("unknown field " + strconv.Quote(field.Name))
} }
@@ -9969,6 +10184,11 @@ func (ec *executionContext) _People(ctx context.Context, sel ast.SelectionSet, o
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
out.Invalids++ out.Invalids++
} }
case "kind":
out.Values[i] = ec._People_kind(ctx, field, obj)
if out.Values[i] == graphql.Null {
out.Invalids++
}
case "createdAt": case "createdAt":
out.Values[i] = ec._People_createdAt(ctx, field, obj) out.Values[i] = ec._People_createdAt(ctx, field, obj)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
@@ -11154,6 +11374,11 @@ func (ec *executionContext) marshalNControlStateTransitionEdge2ᚖgithubᚗcom
return ec._ControlStateTransitionEdge(ctx, sel, v) return ec._ControlStateTransitionEdge(ctx, sel, v)
} }
func (ec *executionContext) unmarshalNCreatePeopleInput2githubᚗcomᚋgetproboᚋproboᚋpkgᚋapiᚋconsoleᚋv1ᚋtypesᚐCreatePeopleInput(ctx context.Context, v any) (types.CreatePeopleInput, error) {
res, err := ec.unmarshalInputCreatePeopleInput(ctx, v)
return res, graphql.ErrorOnPath(ctx, err)
}
func (ec *executionContext) unmarshalNCreateVendorInput2githubᚗcomᚋgetproboᚋproboᚋpkgᚋapiᚋconsoleᚋv1ᚋtypesᚐCreateVendorInput(ctx context.Context, v any) (types.CreateVendorInput, error) { func (ec *executionContext) unmarshalNCreateVendorInput2githubᚗcomᚋgetproboᚋproboᚋpkgᚋapiᚋconsoleᚋv1ᚋtypesᚐCreateVendorInput(ctx context.Context, v any) (types.CreateVendorInput, error) {
res, err := ec.unmarshalInputCreateVendorInput(ctx, v) res, err := ec.unmarshalInputCreateVendorInput(ctx, v)
return res, graphql.ErrorOnPath(ctx, err) return res, graphql.ErrorOnPath(ctx, err)
@@ -11475,6 +11700,10 @@ func (ec *executionContext) marshalNPageInfo2ᚖgithubᚗcomᚋgetproboᚋprobo
return ec._PageInfo(ctx, sel, v) 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 { 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 v == nil {
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
@@ -11547,6 +11776,16 @@ func (ec *executionContext) marshalNPeopleEdge2ᚖgithubᚗcomᚋgetproboᚋprob
return ec._PeopleEdge(ctx, sel, v) return ec._PeopleEdge(ctx, sel, v)
} }
func (ec *executionContext) unmarshalNPeopleKind2githubᚗcomᚋgetproboᚋproboᚋpkgᚋapiᚋconsoleᚋv1ᚋtypesᚐPeopleKind(ctx context.Context, v any) (types.PeopleKind, error) {
var res types.PeopleKind
err := res.UnmarshalGQL(v)
return res, graphql.ErrorOnPath(ctx, err)
}
func (ec *executionContext) marshalNPeopleKind2githubᚗcomᚋgetproboᚋproboᚋpkgᚋapiᚋconsoleᚋv1ᚋtypesᚐPeopleKind(ctx context.Context, sel ast.SelectionSet, v types.PeopleKind) graphql.Marshaler {
return v
}
func (ec *executionContext) unmarshalNString2string(ctx context.Context, v any) (string, error) { func (ec *executionContext) unmarshalNString2string(ctx context.Context, v any) (string, error) {
res, err := graphql.UnmarshalString(v) res, err := graphql.UnmarshalString(v)
return res, graphql.ErrorOnPath(ctx, err) return res, graphql.ErrorOnPath(ctx, err)

View File

@@ -45,6 +45,7 @@ func NewPeople(p *coredata.People) *People {
FullName: p.FullName, FullName: p.FullName,
PrimaryEmailAddress: p.PrimaryEmailAddress, PrimaryEmailAddress: p.PrimaryEmailAddress,
AdditionalEmailAddresses: p.AdditionalEmailAddresses, AdditionalEmailAddresses: p.AdditionalEmailAddresses,
Kind: PeopleKind(p.Kind.String()),
CreatedAt: p.CreatedAt, CreatedAt: p.CreatedAt,
UpdatedAt: p.UpdatedAt, UpdatedAt: p.UpdatedAt,
} }

View File

@@ -60,6 +60,13 @@ type ControlStateTransitionEdge struct {
Node *ControlStateTransition `json:"node"` Node *ControlStateTransition `json:"node"`
} }
type CreatePeopleInput struct {
OrganizationID gid.GID `json:"organizationId"`
FullName string `json:"fullName"`
PrimaryEmailAddress string `json:"primaryEmailAddress"`
Kind PeopleKind `json:"kind"`
}
type CreateVendorInput struct { type CreateVendorInput struct {
OrganizationID gid.GID `json:"organizationId"` OrganizationID gid.GID `json:"organizationId"`
Name string `json:"name"` Name string `json:"name"`
@@ -163,12 +170,13 @@ type PageInfo struct {
} }
type People struct { type People struct {
ID gid.GID `json:"id"` ID gid.GID `json:"id"`
FullName string `json:"fullName"` FullName string `json:"fullName"`
PrimaryEmailAddress string `json:"primaryEmailAddress"` PrimaryEmailAddress string `json:"primaryEmailAddress"`
AdditionalEmailAddresses []string `json:"additionalEmailAddresses"` AdditionalEmailAddresses []string `json:"additionalEmailAddresses"`
CreatedAt time.Time `json:"createdAt"` Kind PeopleKind `json:"kind"`
UpdatedAt time.Time `json:"updatedAt"` CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
} }
func (People) IsNode() {} func (People) IsNode() {}
@@ -338,6 +346,49 @@ func (e EvidenceState) MarshalGQL(w io.Writer) {
fmt.Fprint(w, strconv.Quote(e.String())) fmt.Fprint(w, strconv.Quote(e.String()))
} }
type PeopleKind string
const (
PeopleKindEmployee PeopleKind = "EMPLOYEE"
PeopleKindContractor PeopleKind = "CONTRACTOR"
PeopleKindVendor PeopleKind = "VENDOR"
)
var AllPeopleKind = []PeopleKind{
PeopleKindEmployee,
PeopleKindContractor,
PeopleKindVendor,
}
func (e PeopleKind) IsValid() bool {
switch e {
case PeopleKindEmployee, PeopleKindContractor, PeopleKindVendor:
return true
}
return false
}
func (e PeopleKind) String() string {
return string(e)
}
func (e *PeopleKind) UnmarshalGQL(v any) error {
str, ok := v.(string)
if !ok {
return fmt.Errorf("enums must be strings")
}
*e = PeopleKind(str)
if !e.IsValid() {
return fmt.Errorf("%s is not a valid PeopleKind", str)
}
return nil
}
func (e PeopleKind) MarshalGQL(w io.Writer) {
fmt.Fprint(w, strconv.Quote(e.String()))
}
type TaskState string type TaskState string
const ( const (

View File

@@ -98,6 +98,23 @@ func (r *mutationResolver) DeletePeople(ctx context.Context, input types.DeleteP
return "", nil return "", nil
} }
// CreatePeople is the resolver for the createPeople field.
func (r *mutationResolver) CreatePeople(ctx context.Context, input types.CreatePeopleInput) (*types.People, error) {
people, err := r.svc.CreatePeople(ctx, probo.CreatePeopleRequest{
OrganizationID: input.OrganizationID,
FullName: input.FullName,
PrimaryEmailAddress: input.PrimaryEmailAddress,
AdditionalEmailAddresses: []string{},
Kind: coredata.PeopleKindEmployee,
})
if err != nil {
return nil, fmt.Errorf("cannot create people: %w", err)
}
return types.NewPeople(people), nil
}
// Frameworks is the resolver for the frameworks field. // Frameworks is the resolver for the frameworks field.
func (r *organizationResolver) Frameworks(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.FrameworkConnection, error) { func (r *organizationResolver) Frameworks(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.FrameworkConnection, error) {
cursor := types.NewCursor(first, after, last, before) cursor := types.NewCursor(first, after, last, before)