Add people pages
Signed-off-by: Bryan Frimin <bryan@getprobo.com> Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
committed by
Sacha Al Himdani
parent
6862841459
commit
b94b58e2de
49
apps/console2/src/components/form/EmailsField.tsx
Normal file
49
apps/console2/src/components/form/EmailsField.tsx
Normal file
@@ -0,0 +1,49 @@
|
||||
import { Button, IconPlusLarge, IconTrashCan, Input, Label } from "@probo/ui";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { useFieldArray } from "react-hook-form";
|
||||
import type { Control } from "react-hook-form";
|
||||
import type { UseFormRegister } from "react-hook-form";
|
||||
|
||||
type Props = {
|
||||
control: Control<any>;
|
||||
register: UseFormRegister<any>;
|
||||
};
|
||||
|
||||
/**
|
||||
* A field to handle multiple emails
|
||||
*/
|
||||
export function EmailsField({ control, register }: Props) {
|
||||
const { __ } = useTranslate();
|
||||
const { fields, append, remove } = useFieldArray({
|
||||
name: "additionalEmailAddresses",
|
||||
control,
|
||||
});
|
||||
|
||||
return (
|
||||
<fieldset className="space-y-2">
|
||||
{fields.length > 0 && <Label>{__("Additional emails")}</Label>}
|
||||
{fields.map((field, index) => (
|
||||
<div key={field.id} className="flex items-stretch">
|
||||
<Input
|
||||
className="w-full"
|
||||
{...register(`additionalEmailAddresses.${index}`)}
|
||||
type="email"
|
||||
/>
|
||||
<Button
|
||||
icon={IconTrashCan}
|
||||
variant="tertiary"
|
||||
onClick={() => remove(index)}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
<Button
|
||||
variant="tertiary"
|
||||
type="button"
|
||||
icon={IconPlusLarge}
|
||||
onClick={() => append("")}
|
||||
>
|
||||
{__("Add email")}
|
||||
</Button>
|
||||
</fieldset>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,20 @@
|
||||
import { graphql } from "relay-runtime";
|
||||
import type { PeopleGraphQuery } from "./__generated__/PeopleGraphQuery.graphql";
|
||||
import { useLazyLoadQuery } from "react-relay";
|
||||
import { useMemo } from "react";
|
||||
import {
|
||||
useLazyLoadQuery,
|
||||
useMutation,
|
||||
usePreloadedQuery,
|
||||
useRefetchableFragment,
|
||||
type PreloadedQuery,
|
||||
} from "react-relay";
|
||||
import { useMemo, useTransition } from "react";
|
||||
import type { PeopleGraphPaginatedQuery } from "./__generated__/PeopleGraphPaginatedQuery.graphql";
|
||||
import type { PeopleGraphPaginatedFragment$key } from "./__generated__/PeopleGraphPaginatedFragment.graphql";
|
||||
import { useConfirm } from "@probo/ui";
|
||||
import type { PeopleGraphDeleteMutation } from "./__generated__/PeopleGraphDeleteMutation.graphql";
|
||||
import { sprintf } from "@probo/helpers";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import type { PeopleGraphNodeQuery$data } from "./__generated__/PeopleGraphNodeQuery.graphql";
|
||||
|
||||
const peopleQuery = graphql`
|
||||
query PeopleGraphQuery($organizationId: ID!) {
|
||||
@@ -21,6 +34,9 @@ const peopleQuery = graphql`
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* Return a list of people (used for people selectors)
|
||||
*/
|
||||
export function usePeople(organizationId: string) {
|
||||
const data = useLazyLoadQuery<PeopleGraphQuery>(peopleQuery, {
|
||||
organizationId: organizationId,
|
||||
@@ -29,3 +45,139 @@ export function usePeople(organizationId: string) {
|
||||
return data.organization?.peoples?.edges.map((edge) => edge.node) ?? [];
|
||||
}, [data]);
|
||||
}
|
||||
|
||||
export const paginatedPeopleQuery = graphql`
|
||||
query PeopleGraphPaginatedQuery($organizationId: ID!) {
|
||||
organization: node(id: $organizationId) {
|
||||
... on Organization {
|
||||
id
|
||||
...PeopleGraphPaginatedFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const paginatedPeopleFragment = graphql`
|
||||
fragment PeopleGraphPaginatedFragment on Organization
|
||||
@refetchable(queryName: "PeopleListQuery")
|
||||
@argumentDefinitions(
|
||||
first: { type: "Int", defaultValue: 50 }
|
||||
order: { type: "PeopleOrder", defaultValue: null }
|
||||
after: { type: "CursorKey", defaultValue: null }
|
||||
before: { type: "CursorKey", defaultValue: null }
|
||||
last: { type: "Int", defaultValue: null }
|
||||
) {
|
||||
peoples(
|
||||
first: $first
|
||||
after: $after
|
||||
last: $last
|
||||
before: $before
|
||||
orderBy: $order
|
||||
) @connection(key: "PeopleGraphPaginatedQuery_peoples") {
|
||||
__id
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
fullName
|
||||
primaryEmailAddress
|
||||
kind
|
||||
additionalEmailAddresses
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export function usePeopleQuery(
|
||||
queryRef: PreloadedQuery<PeopleGraphPaginatedQuery>
|
||||
) {
|
||||
const data = usePreloadedQuery(paginatedPeopleQuery, queryRef);
|
||||
const [dataFragment, refetch] = useRefetchableFragment(
|
||||
paginatedPeopleFragment,
|
||||
data.organization as PeopleGraphPaginatedFragment$key
|
||||
);
|
||||
const people = dataFragment?.peoples?.edges.map((edge) => edge.node);
|
||||
return {
|
||||
people,
|
||||
refetch,
|
||||
connectionId: dataFragment.peoples.__id,
|
||||
};
|
||||
}
|
||||
|
||||
export const deletePeopleMutation = graphql`
|
||||
mutation PeopleGraphDeleteMutation(
|
||||
$input: DeletePeopleInput!
|
||||
$connections: [ID!]!
|
||||
) {
|
||||
deletePeople(input: $input) {
|
||||
deletedPeopleId @deleteEdge(connections: $connections)
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const PeopleConnectionKey = "PeopleGraphPaginatedQuery_peoples";
|
||||
|
||||
export const useDeletePeople = (
|
||||
people: { id?: string; fullName?: string },
|
||||
connectionId: string
|
||||
) => {
|
||||
const [mutate] = useMutation<PeopleGraphDeleteMutation>(deletePeopleMutation);
|
||||
const confirm = useConfirm();
|
||||
const { __ } = useTranslate();
|
||||
|
||||
return () => {
|
||||
if (!people.id || !people.fullName) {
|
||||
return alert(__("Failed to delete people: missing id or fullName"));
|
||||
}
|
||||
confirm(
|
||||
() =>
|
||||
new Promise((resolve) => {
|
||||
mutate({
|
||||
variables: {
|
||||
input: {
|
||||
peopleId: people.id!,
|
||||
},
|
||||
connections: [connectionId],
|
||||
},
|
||||
onCompleted: () => resolve(),
|
||||
});
|
||||
}),
|
||||
{
|
||||
message: sprintf(
|
||||
__(
|
||||
'This will permanently delete "%s". This action cannot be undone.'
|
||||
),
|
||||
people.fullName
|
||||
),
|
||||
}
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
export const peopleNodeQuery = graphql`
|
||||
query PeopleGraphNodeQuery($peopleId: ID!) {
|
||||
node(id: $peopleId) {
|
||||
... on People {
|
||||
id
|
||||
fullName
|
||||
primaryEmailAddress
|
||||
kind
|
||||
additionalEmailAddresses
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const updatePeopleMutation = graphql`
|
||||
mutation PeopleGraphUpdateMutation($input: UpdatePeopleInput!) {
|
||||
updatePeople(input: $input) {
|
||||
people {
|
||||
id
|
||||
fullName
|
||||
primaryEmailAddress
|
||||
kind
|
||||
additionalEmailAddresses
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
132
apps/console2/src/hooks/graph/__generated__/PeopleGraphDeleteMutation.graphql.ts
generated
Normal file
132
apps/console2/src/hooks/graph/__generated__/PeopleGraphDeleteMutation.graphql.ts
generated
Normal file
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* @generated SignedSource<<242c546027e5b7c8410fca920a4dfa3c>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type DeletePeopleInput = {
|
||||
peopleId: string;
|
||||
};
|
||||
export type PeopleGraphDeleteMutation$variables = {
|
||||
connections: ReadonlyArray<string>;
|
||||
input: DeletePeopleInput;
|
||||
};
|
||||
export type PeopleGraphDeleteMutation$data = {
|
||||
readonly deletePeople: {
|
||||
readonly deletedPeopleId: string;
|
||||
};
|
||||
};
|
||||
export type PeopleGraphDeleteMutation = {
|
||||
response: PeopleGraphDeleteMutation$data;
|
||||
variables: PeopleGraphDeleteMutation$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = {
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "connections"
|
||||
},
|
||||
v1 = {
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "input"
|
||||
},
|
||||
v2 = [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "input",
|
||||
"variableName": "input"
|
||||
}
|
||||
],
|
||||
v3 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "deletedPeopleId",
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": [
|
||||
(v0/*: any*/),
|
||||
(v1/*: any*/)
|
||||
],
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "PeopleGraphDeleteMutation",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v2/*: any*/),
|
||||
"concreteType": "DeletePeoplePayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "deletePeople",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": [
|
||||
(v1/*: any*/),
|
||||
(v0/*: any*/)
|
||||
],
|
||||
"kind": "Operation",
|
||||
"name": "PeopleGraphDeleteMutation",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v2/*: any*/),
|
||||
"concreteType": "DeletePeoplePayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "deletePeople",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"filters": null,
|
||||
"handle": "deleteEdge",
|
||||
"key": "",
|
||||
"kind": "ScalarHandle",
|
||||
"name": "deletedPeopleId",
|
||||
"handleArgs": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "connections",
|
||||
"variableName": "connections"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "9e0f5fd398468199333312505f258bb2",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "PeopleGraphDeleteMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation PeopleGraphDeleteMutation(\n $input: DeletePeopleInput!\n) {\n deletePeople(input: $input) {\n deletedPeopleId\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "611aa91bbb039c4e5800838f5db7985c";
|
||||
|
||||
export default node;
|
||||
165
apps/console2/src/hooks/graph/__generated__/PeopleGraphNodeQuery.graphql.ts
generated
Normal file
165
apps/console2/src/hooks/graph/__generated__/PeopleGraphNodeQuery.graphql.ts
generated
Normal file
@@ -0,0 +1,165 @@
|
||||
/**
|
||||
* @generated SignedSource<<006f8f3dc3c4d41a23cc71a870aa571c>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type PeopleKind = "CONTRACTOR" | "EMPLOYEE" | "SERVICE_ACCOUNT";
|
||||
export type PeopleGraphNodeQuery$variables = {
|
||||
peopleId: string;
|
||||
};
|
||||
export type PeopleGraphNodeQuery$data = {
|
||||
readonly node: {
|
||||
readonly additionalEmailAddresses?: ReadonlyArray<string>;
|
||||
readonly fullName?: string;
|
||||
readonly id?: string;
|
||||
readonly kind?: PeopleKind;
|
||||
readonly primaryEmailAddress?: string;
|
||||
};
|
||||
};
|
||||
export type PeopleGraphNodeQuery = {
|
||||
response: PeopleGraphNodeQuery$data;
|
||||
variables: PeopleGraphNodeQuery$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "peopleId"
|
||||
}
|
||||
],
|
||||
v1 = [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "id",
|
||||
"variableName": "peopleId"
|
||||
}
|
||||
],
|
||||
v2 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
v3 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "fullName",
|
||||
"storageKey": null
|
||||
},
|
||||
v4 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "primaryEmailAddress",
|
||||
"storageKey": null
|
||||
},
|
||||
v5 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "kind",
|
||||
"storageKey": null
|
||||
},
|
||||
v6 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "additionalEmailAddresses",
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "PeopleGraphNodeQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v1/*: any*/),
|
||||
"concreteType": null,
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"kind": "InlineFragment",
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/),
|
||||
(v4/*: any*/),
|
||||
(v5/*: any*/),
|
||||
(v6/*: any*/)
|
||||
],
|
||||
"type": "People",
|
||||
"abstractKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Query",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "PeopleGraphNodeQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"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": [
|
||||
(v3/*: any*/),
|
||||
(v4/*: any*/),
|
||||
(v5/*: any*/),
|
||||
(v6/*: any*/)
|
||||
],
|
||||
"type": "People",
|
||||
"abstractKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "319f5f4ff11db061920a25bbfda43244",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "PeopleGraphNodeQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query PeopleGraphNodeQuery(\n $peopleId: ID!\n) {\n node(id: $peopleId) {\n __typename\n ... on People {\n id\n fullName\n primaryEmailAddress\n kind\n additionalEmailAddresses\n }\n id\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "b4e6fe403e30fbc27ae638d464ad7543";
|
||||
|
||||
export default node;
|
||||
252
apps/console2/src/hooks/graph/__generated__/PeopleGraphPaginatedFragment.graphql.ts
generated
Normal file
252
apps/console2/src/hooks/graph/__generated__/PeopleGraphPaginatedFragment.graphql.ts
generated
Normal file
@@ -0,0 +1,252 @@
|
||||
/**
|
||||
* @generated SignedSource<<e4fcd65491a7570cf56382a3f977e06d>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ReaderFragment } from 'relay-runtime';
|
||||
export type PeopleKind = "CONTRACTOR" | "EMPLOYEE" | "SERVICE_ACCOUNT";
|
||||
import { FragmentRefs } from "relay-runtime";
|
||||
export type PeopleGraphPaginatedFragment$data = {
|
||||
readonly id: string;
|
||||
readonly peoples: {
|
||||
readonly __id: string;
|
||||
readonly edges: ReadonlyArray<{
|
||||
readonly node: {
|
||||
readonly additionalEmailAddresses: ReadonlyArray<string>;
|
||||
readonly fullName: string;
|
||||
readonly id: string;
|
||||
readonly kind: PeopleKind;
|
||||
readonly primaryEmailAddress: string;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
readonly " $fragmentType": "PeopleGraphPaginatedFragment";
|
||||
};
|
||||
export type PeopleGraphPaginatedFragment$key = {
|
||||
readonly " $data"?: PeopleGraphPaginatedFragment$data;
|
||||
readonly " $fragmentSpreads": FragmentRefs<"PeopleGraphPaginatedFragment">;
|
||||
};
|
||||
|
||||
import PeopleListQuery_graphql from './PeopleListQuery.graphql';
|
||||
|
||||
const node: ReaderFragment = (function(){
|
||||
var v0 = [
|
||||
"peoples"
|
||||
],
|
||||
v1 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"argumentDefinitions": [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "after"
|
||||
},
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "before"
|
||||
},
|
||||
{
|
||||
"defaultValue": 50,
|
||||
"kind": "LocalArgument",
|
||||
"name": "first"
|
||||
},
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "last"
|
||||
},
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "order"
|
||||
}
|
||||
],
|
||||
"kind": "Fragment",
|
||||
"metadata": {
|
||||
"connection": [
|
||||
{
|
||||
"count": null,
|
||||
"cursor": null,
|
||||
"direction": "bidirectional",
|
||||
"path": (v0/*: any*/)
|
||||
}
|
||||
],
|
||||
"refetch": {
|
||||
"connection": {
|
||||
"forward": {
|
||||
"count": "first",
|
||||
"cursor": "after"
|
||||
},
|
||||
"backward": {
|
||||
"count": "last",
|
||||
"cursor": "before"
|
||||
},
|
||||
"path": (v0/*: any*/)
|
||||
},
|
||||
"fragmentPathInResult": [
|
||||
"node"
|
||||
],
|
||||
"operation": PeopleListQuery_graphql,
|
||||
"identifierInfo": {
|
||||
"identifierField": "id",
|
||||
"identifierQueryVariableName": "id"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "PeopleGraphPaginatedFragment",
|
||||
"selections": [
|
||||
{
|
||||
"alias": "peoples",
|
||||
"args": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "orderBy",
|
||||
"variableName": "order"
|
||||
}
|
||||
],
|
||||
"concreteType": "PeopleConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "__PeopleGraphPaginatedQuery_peoples_connection",
|
||||
"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": [
|
||||
(v1/*: any*/),
|
||||
{
|
||||
"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
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "additionalEmailAddresses",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "__typename",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "cursor",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "PageInfo",
|
||||
"kind": "LinkedField",
|
||||
"name": "pageInfo",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "endCursor",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "hasNextPage",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "hasPreviousPage",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "startCursor",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"kind": "ClientExtension",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "__id",
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
(v1/*: any*/)
|
||||
],
|
||||
"type": "Organization",
|
||||
"abstractKey": null
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "c163b7909a337efd22088852b9908045";
|
||||
|
||||
export default node;
|
||||
271
apps/console2/src/hooks/graph/__generated__/PeopleGraphPaginatedQuery.graphql.ts
generated
Normal file
271
apps/console2/src/hooks/graph/__generated__/PeopleGraphPaginatedQuery.graphql.ts
generated
Normal file
@@ -0,0 +1,271 @@
|
||||
/**
|
||||
* @generated SignedSource<<43878b22fe8a689f41bcd506711ddb5f>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
import { FragmentRefs } from "relay-runtime";
|
||||
export type PeopleGraphPaginatedQuery$variables = {
|
||||
organizationId: string;
|
||||
};
|
||||
export type PeopleGraphPaginatedQuery$data = {
|
||||
readonly organization: {
|
||||
readonly id?: string;
|
||||
readonly " $fragmentSpreads": FragmentRefs<"PeopleGraphPaginatedFragment">;
|
||||
};
|
||||
};
|
||||
export type PeopleGraphPaginatedQuery = {
|
||||
response: PeopleGraphPaginatedQuery$data;
|
||||
variables: PeopleGraphPaginatedQuery$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
|
||||
},
|
||||
v3 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "__typename",
|
||||
"storageKey": null
|
||||
},
|
||||
v4 = [
|
||||
{
|
||||
"kind": "Literal",
|
||||
"name": "first",
|
||||
"value": 50
|
||||
}
|
||||
];
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "PeopleGraphPaginatedQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": "organization",
|
||||
"args": (v1/*: any*/),
|
||||
"concreteType": null,
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"kind": "InlineFragment",
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
{
|
||||
"args": null,
|
||||
"kind": "FragmentSpread",
|
||||
"name": "PeopleGraphPaginatedFragment"
|
||||
}
|
||||
],
|
||||
"type": "Organization",
|
||||
"abstractKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Query",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "PeopleGraphPaginatedQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": "organization",
|
||||
"args": (v1/*: any*/),
|
||||
"concreteType": null,
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/),
|
||||
(v2/*: any*/),
|
||||
{
|
||||
"kind": "InlineFragment",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v4/*: any*/),
|
||||
"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
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "kind",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "additionalEmailAddresses",
|
||||
"storageKey": null
|
||||
},
|
||||
(v3/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "cursor",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "PageInfo",
|
||||
"kind": "LinkedField",
|
||||
"name": "pageInfo",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "endCursor",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "hasNextPage",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "hasPreviousPage",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "startCursor",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"kind": "ClientExtension",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "__id",
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"storageKey": "peoples(first:50)"
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v4/*: any*/),
|
||||
"filters": [
|
||||
"orderBy"
|
||||
],
|
||||
"handle": "connection",
|
||||
"key": "PeopleGraphPaginatedQuery_peoples",
|
||||
"kind": "LinkedHandle",
|
||||
"name": "peoples"
|
||||
}
|
||||
],
|
||||
"type": "Organization",
|
||||
"abstractKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "b154e94ac7e783a492bc5aed7d44229d",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "PeopleGraphPaginatedQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query PeopleGraphPaginatedQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n ... on Organization {\n id\n ...PeopleGraphPaginatedFragment\n }\n id\n }\n}\n\nfragment PeopleGraphPaginatedFragment on Organization {\n peoples(first: 50) {\n edges {\n node {\n id\n fullName\n primaryEmailAddress\n kind\n additionalEmailAddresses\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "2d5f8ae64618a57eeb623b0ade941486";
|
||||
|
||||
export default node;
|
||||
142
apps/console2/src/hooks/graph/__generated__/PeopleGraphUpdateMutation.graphql.ts
generated
Normal file
142
apps/console2/src/hooks/graph/__generated__/PeopleGraphUpdateMutation.graphql.ts
generated
Normal file
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
* @generated SignedSource<<bbf8c99c9d2d556129349cfe82d26582>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type PeopleKind = "CONTRACTOR" | "EMPLOYEE" | "SERVICE_ACCOUNT";
|
||||
export type UpdatePeopleInput = {
|
||||
additionalEmailAddresses?: ReadonlyArray<string> | null | undefined;
|
||||
fullName?: string | null | undefined;
|
||||
id: string;
|
||||
kind?: PeopleKind | null | undefined;
|
||||
primaryEmailAddress?: string | null | undefined;
|
||||
};
|
||||
export type PeopleGraphUpdateMutation$variables = {
|
||||
input: UpdatePeopleInput;
|
||||
};
|
||||
export type PeopleGraphUpdateMutation$data = {
|
||||
readonly updatePeople: {
|
||||
readonly people: {
|
||||
readonly additionalEmailAddresses: ReadonlyArray<string>;
|
||||
readonly fullName: string;
|
||||
readonly id: string;
|
||||
readonly kind: PeopleKind;
|
||||
readonly primaryEmailAddress: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
export type PeopleGraphUpdateMutation = {
|
||||
response: PeopleGraphUpdateMutation$data;
|
||||
variables: PeopleGraphUpdateMutation$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "input"
|
||||
}
|
||||
],
|
||||
v1 = [
|
||||
{
|
||||
"alias": null,
|
||||
"args": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "input",
|
||||
"variableName": "input"
|
||||
}
|
||||
],
|
||||
"concreteType": "UpdatePeoplePayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "updatePeople",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "People",
|
||||
"kind": "LinkedField",
|
||||
"name": "people",
|
||||
"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
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "additionalEmailAddresses",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
];
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "PeopleGraphUpdateMutation",
|
||||
"selections": (v1/*: any*/),
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "PeopleGraphUpdateMutation",
|
||||
"selections": (v1/*: any*/)
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "c61ca0a6b20067ce5e0c708fc55509d8",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "PeopleGraphUpdateMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation PeopleGraphUpdateMutation(\n $input: UpdatePeopleInput!\n) {\n updatePeople(input: $input) {\n people {\n id\n fullName\n primaryEmailAddress\n kind\n additionalEmailAddresses\n }\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "48c2618963704a8cd3b1f0a68817cdad";
|
||||
|
||||
export default node;
|
||||
344
apps/console2/src/hooks/graph/__generated__/PeopleListQuery.graphql.ts
generated
Normal file
344
apps/console2/src/hooks/graph/__generated__/PeopleListQuery.graphql.ts
generated
Normal file
@@ -0,0 +1,344 @@
|
||||
/**
|
||||
* @generated SignedSource<<bc0fdc02fb8b596386e290ae0cf0b8b8>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
import { FragmentRefs } from "relay-runtime";
|
||||
export type OrderDirection = "ASC" | "DESC";
|
||||
export type PeopleOrderField = "CREATED_AT" | "FULL_NAME";
|
||||
export type PeopleOrder = {
|
||||
direction: OrderDirection;
|
||||
field: PeopleOrderField;
|
||||
};
|
||||
export type PeopleListQuery$variables = {
|
||||
after?: any | null | undefined;
|
||||
before?: any | null | undefined;
|
||||
first?: number | null | undefined;
|
||||
id: string;
|
||||
last?: number | null | undefined;
|
||||
order?: PeopleOrder | null | undefined;
|
||||
};
|
||||
export type PeopleListQuery$data = {
|
||||
readonly node: {
|
||||
readonly " $fragmentSpreads": FragmentRefs<"PeopleGraphPaginatedFragment">;
|
||||
};
|
||||
};
|
||||
export type PeopleListQuery = {
|
||||
response: PeopleListQuery$data;
|
||||
variables: PeopleListQuery$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = {
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "after"
|
||||
},
|
||||
v1 = {
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "before"
|
||||
},
|
||||
v2 = {
|
||||
"defaultValue": 50,
|
||||
"kind": "LocalArgument",
|
||||
"name": "first"
|
||||
},
|
||||
v3 = {
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "id"
|
||||
},
|
||||
v4 = {
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "last"
|
||||
},
|
||||
v5 = {
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "order"
|
||||
},
|
||||
v6 = [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "id",
|
||||
"variableName": "id"
|
||||
}
|
||||
],
|
||||
v7 = {
|
||||
"kind": "Variable",
|
||||
"name": "after",
|
||||
"variableName": "after"
|
||||
},
|
||||
v8 = {
|
||||
"kind": "Variable",
|
||||
"name": "before",
|
||||
"variableName": "before"
|
||||
},
|
||||
v9 = {
|
||||
"kind": "Variable",
|
||||
"name": "first",
|
||||
"variableName": "first"
|
||||
},
|
||||
v10 = {
|
||||
"kind": "Variable",
|
||||
"name": "last",
|
||||
"variableName": "last"
|
||||
},
|
||||
v11 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "__typename",
|
||||
"storageKey": null
|
||||
},
|
||||
v12 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
v13 = [
|
||||
(v7/*: any*/),
|
||||
(v8/*: any*/),
|
||||
(v9/*: any*/),
|
||||
(v10/*: any*/),
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "orderBy",
|
||||
"variableName": "order"
|
||||
}
|
||||
];
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": [
|
||||
(v0/*: any*/),
|
||||
(v1/*: any*/),
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/),
|
||||
(v4/*: any*/),
|
||||
(v5/*: any*/)
|
||||
],
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "PeopleListQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v6/*: any*/),
|
||||
"concreteType": null,
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"args": [
|
||||
(v7/*: any*/),
|
||||
(v8/*: any*/),
|
||||
(v9/*: any*/),
|
||||
(v10/*: any*/),
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "order",
|
||||
"variableName": "order"
|
||||
}
|
||||
],
|
||||
"kind": "FragmentSpread",
|
||||
"name": "PeopleGraphPaginatedFragment"
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Query",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": [
|
||||
(v0/*: any*/),
|
||||
(v1/*: any*/),
|
||||
(v2/*: any*/),
|
||||
(v4/*: any*/),
|
||||
(v5/*: any*/),
|
||||
(v3/*: any*/)
|
||||
],
|
||||
"kind": "Operation",
|
||||
"name": "PeopleListQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v6/*: any*/),
|
||||
"concreteType": null,
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v11/*: any*/),
|
||||
(v12/*: any*/),
|
||||
{
|
||||
"kind": "InlineFragment",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v13/*: any*/),
|
||||
"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": [
|
||||
(v12/*: any*/),
|
||||
{
|
||||
"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
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "additionalEmailAddresses",
|
||||
"storageKey": null
|
||||
},
|
||||
(v11/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "cursor",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "PageInfo",
|
||||
"kind": "LinkedField",
|
||||
"name": "pageInfo",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "endCursor",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "hasNextPage",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "hasPreviousPage",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "startCursor",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"kind": "ClientExtension",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "__id",
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v13/*: any*/),
|
||||
"filters": [
|
||||
"orderBy"
|
||||
],
|
||||
"handle": "connection",
|
||||
"key": "PeopleGraphPaginatedQuery_peoples",
|
||||
"kind": "LinkedHandle",
|
||||
"name": "peoples"
|
||||
}
|
||||
],
|
||||
"type": "Organization",
|
||||
"abstractKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "a17ff68897dbcf3daa33b073bc83f1bb",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "PeopleListQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query PeopleListQuery(\n $after: CursorKey = null\n $before: CursorKey = null\n $first: Int = 50\n $last: Int = null\n $order: PeopleOrder = null\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...PeopleGraphPaginatedFragment_16fISc\n id\n }\n}\n\nfragment PeopleGraphPaginatedFragment_16fISc on Organization {\n peoples(first: $first, after: $after, last: $last, before: $before, orderBy: $order) {\n edges {\n node {\n id\n fullName\n primaryEmailAddress\n kind\n additionalEmailAddresses\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "c163b7909a337efd22088852b9908045";
|
||||
|
||||
export default node;
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
ActionDropdown,
|
||||
Button,
|
||||
Card,
|
||||
ConfirmDialog,
|
||||
DropdownItem,
|
||||
FileButton,
|
||||
IconChevronDown,
|
||||
@@ -27,7 +26,7 @@ import {
|
||||
Th,
|
||||
Thead,
|
||||
Tr,
|
||||
useConfirmDialogRef,
|
||||
useConfirm,
|
||||
} from "@probo/ui";
|
||||
import {
|
||||
measuresQuery,
|
||||
@@ -245,33 +244,33 @@ type MeasureRowProps = {
|
||||
function MeasureRow(props: MeasureRowProps) {
|
||||
const { __ } = useTranslate();
|
||||
const [deleteMeasure, isDeleting] = useDeleteMeasureMutation();
|
||||
const confirm = useConfirm();
|
||||
|
||||
const onDelete = () => {
|
||||
return new Promise<void>((resolve) => {
|
||||
deleteMeasure({
|
||||
variables: {
|
||||
input: { measureId: props.measure.id },
|
||||
connections: [props.connectionId],
|
||||
},
|
||||
onCompleted: () => resolve(),
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const confirmRef = useConfirmDialogRef();
|
||||
|
||||
return (
|
||||
<>
|
||||
<ConfirmDialog
|
||||
message={sprintf(
|
||||
confirm(
|
||||
() =>
|
||||
new Promise<void>((resolve) => {
|
||||
deleteMeasure({
|
||||
variables: {
|
||||
input: { measureId: props.measure.id },
|
||||
connections: [props.connectionId],
|
||||
},
|
||||
onCompleted: () => resolve(),
|
||||
});
|
||||
}),
|
||||
{
|
||||
message: sprintf(
|
||||
__(
|
||||
'This will permanently delete the measure "%s". This action cannot be undone.'
|
||||
),
|
||||
props.measure.name
|
||||
)}
|
||||
onConfirm={onDelete}
|
||||
ref={confirmRef}
|
||||
/>
|
||||
),
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tr>
|
||||
<Td>{props.measure.name}</Td>
|
||||
<Td width={120}>
|
||||
@@ -281,7 +280,7 @@ function MeasureRow(props: MeasureRowProps) {
|
||||
<ActionDropdown>
|
||||
<DropdownItem icon={IconPencil}>{__("Edit")}</DropdownItem>
|
||||
<DropdownItem
|
||||
onClick={() => confirmRef.current?.open()}
|
||||
onClick={onDelete}
|
||||
disabled={isDeleting}
|
||||
variant="danger"
|
||||
icon={IconTrashCan}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
import {
|
||||
ConnectionHandler,
|
||||
usePreloadedQuery,
|
||||
type PreloadedQuery,
|
||||
} from "react-relay";
|
||||
import type { PeopleGraphNodeQuery } from "/hooks/graph/__generated__/PeopleGraphNodeQuery.graphql";
|
||||
import {
|
||||
PeopleConnectionKey,
|
||||
peopleNodeQuery,
|
||||
useDeletePeople,
|
||||
} from "/hooks/graph/PeopleGraph";
|
||||
import {
|
||||
ActionDropdown,
|
||||
Avatar,
|
||||
Breadcrumb,
|
||||
DropdownItem,
|
||||
IconTrashCan,
|
||||
TabLink,
|
||||
Tabs,
|
||||
} from "@probo/ui";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { useOrganizationId } from "/hooks/useOrganizationId";
|
||||
import { Outlet } from "react-router";
|
||||
|
||||
type Props = {
|
||||
queryRef: PreloadedQuery<PeopleGraphNodeQuery>;
|
||||
};
|
||||
|
||||
export default function PeopleDetailPage(props: Props) {
|
||||
const data = usePreloadedQuery(peopleNodeQuery, props.queryRef);
|
||||
const people = data.node;
|
||||
const { __ } = useTranslate();
|
||||
const organizationId = useOrganizationId();
|
||||
const deletePeople = useDeletePeople(
|
||||
people,
|
||||
ConnectionHandler.getConnectionID(organizationId, PeopleConnectionKey)
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<Breadcrumb
|
||||
items={[
|
||||
{
|
||||
label: __("People"),
|
||||
to: `/organizations/${organizationId}/people`,
|
||||
},
|
||||
{
|
||||
label: data.node.fullName ?? "",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<div className="flex justify-between">
|
||||
<div className="space-y-4">
|
||||
<Avatar name={people.fullName ?? ""} size="xl" />
|
||||
<div className="text-2xl">{people.fullName}</div>
|
||||
</div>
|
||||
<ActionDropdown variant="secondary">
|
||||
<DropdownItem
|
||||
variant="danger"
|
||||
icon={IconTrashCan}
|
||||
onClick={deletePeople}
|
||||
>
|
||||
{__("Delete")}
|
||||
</DropdownItem>
|
||||
</ActionDropdown>
|
||||
</div>
|
||||
|
||||
<Tabs>
|
||||
<TabLink
|
||||
to={`/organizations/${organizationId}/people/${people.id}/tasks`}
|
||||
>
|
||||
{__("Tasks")}
|
||||
</TabLink>
|
||||
<TabLink
|
||||
to={`/organizations/${organizationId}/people/${people.id}/role`}
|
||||
>
|
||||
{__("Role & access")}
|
||||
</TabLink>
|
||||
<TabLink
|
||||
to={`/organizations/${organizationId}/people/${people.id}/profile`}
|
||||
>
|
||||
{__("General information")}
|
||||
</TabLink>
|
||||
</Tabs>
|
||||
|
||||
<Outlet context={{ people }} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
127
apps/console2/src/pages/organizations/people/PeopleListPage.tsx
Normal file
127
apps/console2/src/pages/organizations/people/PeopleListPage.tsx
Normal file
@@ -0,0 +1,127 @@
|
||||
import {
|
||||
Button,
|
||||
IconPlusLarge,
|
||||
PageHeader,
|
||||
Thead,
|
||||
Tbody,
|
||||
Tr,
|
||||
Th,
|
||||
Td,
|
||||
Avatar,
|
||||
ActionDropdown,
|
||||
DropdownItem,
|
||||
IconPencil,
|
||||
IconTrashCan,
|
||||
useConfirm,
|
||||
} from "@probo/ui";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import type { PeopleGraphPaginatedQuery } from "/hooks/graph/__generated__/PeopleGraphPaginatedQuery.graphql";
|
||||
import { useMutation, type PreloadedQuery } from "react-relay";
|
||||
import {
|
||||
deletePeopleMutation,
|
||||
useDeletePeople,
|
||||
usePeopleQuery,
|
||||
} from "/hooks/graph/PeopleGraph";
|
||||
import { SortableTable, SortableTh } from "/components/SortableTable";
|
||||
import type { PeopleGraphPaginatedFragment$data } from "/hooks/graph/__generated__/PeopleGraphPaginatedFragment.graphql";
|
||||
import type { NodeOf } from "/types";
|
||||
import { usePageTitle } from "@probo/hooks";
|
||||
import { getRole, sprintf } from "@probo/helpers";
|
||||
import { CreatePeopleDialog } from "./dialogs/CreatePeopleDialog";
|
||||
import type { PeopleGraphDeleteMutation } from "/hooks/graph/__generated__/PeopleGraphDeleteMutation.graphql";
|
||||
import { useOrganizationId } from "/hooks/useOrganizationId";
|
||||
import { Link } from "react-router";
|
||||
|
||||
type People = NodeOf<PeopleGraphPaginatedFragment$data["peoples"]>;
|
||||
|
||||
export default function PeopleListPage({
|
||||
queryRef,
|
||||
}: {
|
||||
queryRef: PreloadedQuery<PeopleGraphPaginatedQuery>;
|
||||
}) {
|
||||
const { __ } = useTranslate();
|
||||
const { people, refetch, connectionId } = usePeopleQuery(queryRef);
|
||||
|
||||
usePageTitle(__("Members"));
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<PageHeader
|
||||
title={__("Members")}
|
||||
description={__(
|
||||
"Keep track of your company's workforce and their progress towards completing tasks assigned to them."
|
||||
)}
|
||||
>
|
||||
<CreatePeopleDialog connectionId={connectionId}>
|
||||
<Button icon={IconPlusLarge}>{__("Add member")}</Button>
|
||||
</CreatePeopleDialog>
|
||||
</PageHeader>
|
||||
<SortableTable refetch={refetch}>
|
||||
<Thead>
|
||||
<Tr>
|
||||
<SortableTh field="FULL_NAME">{__("Name")}</SortableTh>
|
||||
<SortableTh field="KIND">{__("Role")}</SortableTh>
|
||||
<Th>{__("Actions")}</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{people.map((person) => (
|
||||
<PeopleRow
|
||||
key={person.id}
|
||||
people={person}
|
||||
connectionId={connectionId}
|
||||
/>
|
||||
))}
|
||||
</Tbody>
|
||||
</SortableTable>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function PeopleRow({
|
||||
people,
|
||||
connectionId,
|
||||
}: {
|
||||
people: People;
|
||||
connectionId: string;
|
||||
}) {
|
||||
const organizationId = useOrganizationId();
|
||||
const { __ } = useTranslate();
|
||||
const deletePeople = useDeletePeople(people, connectionId);
|
||||
|
||||
return (
|
||||
<Tr to={`/organizations/${organizationId}/people/${people.id}/tasks`}>
|
||||
<Td>
|
||||
<div className="flex gap-3 items-center">
|
||||
<Avatar name={people.fullName} />
|
||||
<div>
|
||||
<div className="text-sm">{people.fullName}</div>
|
||||
<div className="text-xs text-txt-tertiary">
|
||||
{people.primaryEmailAddress}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Td>
|
||||
<Td className="text-sm">{getRole(__, people.kind)}</Td>
|
||||
<Td noLink width={50} className="text-end">
|
||||
<ActionDropdown>
|
||||
<DropdownItem asChild>
|
||||
<Link
|
||||
to={`/organizations/${organizationId}/people/${people.id}/tasks`}
|
||||
>
|
||||
<IconPencil size={16} />
|
||||
{__("Edit")}
|
||||
</Link>
|
||||
</DropdownItem>
|
||||
<DropdownItem
|
||||
icon={IconTrashCan}
|
||||
variant="danger"
|
||||
onClick={deletePeople}
|
||||
>
|
||||
{__("Delete")}
|
||||
</DropdownItem>
|
||||
</ActionDropdown>
|
||||
</Td>
|
||||
</Tr>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import {
|
||||
Breadcrumb,
|
||||
Button,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
Field,
|
||||
Option,
|
||||
useDialogRef,
|
||||
} from "@probo/ui";
|
||||
import type { ReactNode } from "react";
|
||||
import { useOrganizationId } from "/hooks/useOrganizationId";
|
||||
import z from "zod";
|
||||
import { getRoles, peopleRoles } from "@probo/helpers";
|
||||
import { useFormWithSchema } from "/hooks/useFormWithSchema";
|
||||
import { graphql } from "relay-runtime";
|
||||
import { ControlledField } from "/components/form/ControlledField";
|
||||
import { EmailsField } from "/components/form/EmailsField";
|
||||
import { useMutationWithToasts } from "/hooks/useMutationWithToasts";
|
||||
|
||||
type Props = {
|
||||
children: ReactNode;
|
||||
connectionId: string;
|
||||
};
|
||||
|
||||
const schema = z.object({
|
||||
fullName: z.string().min(1),
|
||||
primaryEmailAddress: z.string().email(),
|
||||
additionalEmailAddresses: z.preprocess(
|
||||
// Empty additional emails are skipped
|
||||
(v) => (v as string[]).filter((v) => !!v),
|
||||
z.array(z.string().email())
|
||||
),
|
||||
kind: z.enum(peopleRoles),
|
||||
});
|
||||
|
||||
export const createPeopleMutation = graphql`
|
||||
mutation CreatePeopleDialogMutation(
|
||||
$input: CreatePeopleInput!
|
||||
$connections: [ID!]!
|
||||
) {
|
||||
createPeople(input: $input) {
|
||||
peopleEdge @prependEdge(connections: $connections) {
|
||||
node {
|
||||
id
|
||||
fullName
|
||||
primaryEmailAddress
|
||||
kind
|
||||
additionalEmailAddresses
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export function CreatePeopleDialog({ children, connectionId }: Props) {
|
||||
const { __ } = useTranslate();
|
||||
const organizationId = useOrganizationId();
|
||||
const { control, handleSubmit, register } = useFormWithSchema(schema, {
|
||||
defaultValues: {
|
||||
additionalEmailAddresses: [],
|
||||
},
|
||||
});
|
||||
const ref = useDialogRef();
|
||||
|
||||
const [mutate, isMutating] = useMutationWithToasts(createPeopleMutation, {
|
||||
successMessage: __("Person created successfully."),
|
||||
errorMessage: __("Failed to create person. Please try again."),
|
||||
});
|
||||
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
mutate({
|
||||
variables: {
|
||||
input: {
|
||||
...data,
|
||||
organizationId,
|
||||
},
|
||||
connections: [connectionId],
|
||||
},
|
||||
onSuccess: () => {
|
||||
ref.current?.close();
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
ref={ref}
|
||||
trigger={children}
|
||||
title={<Breadcrumb items={[__("People"), __("New Person")]} />}
|
||||
>
|
||||
<form onSubmit={onSubmit}>
|
||||
<DialogContent padded className="space-y-4">
|
||||
<Field
|
||||
label={__("Full name")}
|
||||
{...register("fullName")}
|
||||
type="text"
|
||||
/>
|
||||
<Field
|
||||
label={__("Primary email")}
|
||||
{...register("primaryEmailAddress")}
|
||||
type="email"
|
||||
/>
|
||||
<ControlledField
|
||||
control={control}
|
||||
name="kind"
|
||||
type="select"
|
||||
label={__("Role")}
|
||||
>
|
||||
{getRoles(__).map((role) => (
|
||||
<Option key={role.value} value={role.value}>
|
||||
{role.label}
|
||||
</Option>
|
||||
))}
|
||||
</ControlledField>
|
||||
<EmailsField control={control} register={register} />
|
||||
</DialogContent>
|
||||
<DialogFooter>
|
||||
<Button disabled={isMutating} type="submit">
|
||||
{__("Create")}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
195
apps/console2/src/pages/organizations/people/dialogs/__generated__/CreatePeopleDialogMutation.graphql.ts
generated
Normal file
195
apps/console2/src/pages/organizations/people/dialogs/__generated__/CreatePeopleDialogMutation.graphql.ts
generated
Normal file
@@ -0,0 +1,195 @@
|
||||
/**
|
||||
* @generated SignedSource<<aa74d7b1c9c34a3780220590119aa78f>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type PeopleKind = "CONTRACTOR" | "EMPLOYEE" | "SERVICE_ACCOUNT";
|
||||
export type CreatePeopleInput = {
|
||||
additionalEmailAddresses?: ReadonlyArray<string> | null | undefined;
|
||||
fullName: string;
|
||||
kind: PeopleKind;
|
||||
organizationId: string;
|
||||
primaryEmailAddress: string;
|
||||
};
|
||||
export type CreatePeopleDialogMutation$variables = {
|
||||
connections: ReadonlyArray<string>;
|
||||
input: CreatePeopleInput;
|
||||
};
|
||||
export type CreatePeopleDialogMutation$data = {
|
||||
readonly createPeople: {
|
||||
readonly peopleEdge: {
|
||||
readonly node: {
|
||||
readonly additionalEmailAddresses: ReadonlyArray<string>;
|
||||
readonly fullName: string;
|
||||
readonly id: string;
|
||||
readonly kind: PeopleKind;
|
||||
readonly primaryEmailAddress: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
export type CreatePeopleDialogMutation = {
|
||||
response: CreatePeopleDialogMutation$data;
|
||||
variables: CreatePeopleDialogMutation$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = {
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "connections"
|
||||
},
|
||||
v1 = {
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "input"
|
||||
},
|
||||
v2 = [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "input",
|
||||
"variableName": "input"
|
||||
}
|
||||
],
|
||||
v3 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "PeopleEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "peopleEdge",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "People",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"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
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "additionalEmailAddresses",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": [
|
||||
(v0/*: any*/),
|
||||
(v1/*: any*/)
|
||||
],
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "CreatePeopleDialogMutation",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v2/*: any*/),
|
||||
"concreteType": "CreatePeoplePayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "createPeople",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": [
|
||||
(v1/*: any*/),
|
||||
(v0/*: any*/)
|
||||
],
|
||||
"kind": "Operation",
|
||||
"name": "CreatePeopleDialogMutation",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v2/*: any*/),
|
||||
"concreteType": "CreatePeoplePayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "createPeople",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"filters": null,
|
||||
"handle": "prependEdge",
|
||||
"key": "",
|
||||
"kind": "LinkedHandle",
|
||||
"name": "peopleEdge",
|
||||
"handleArgs": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "connections",
|
||||
"variableName": "connections"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "634c2845825a02a0343c3211b41c90a0",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "CreatePeopleDialogMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation CreatePeopleDialogMutation(\n $input: CreatePeopleInput!\n) {\n createPeople(input: $input) {\n peopleEdge {\n node {\n id\n fullName\n primaryEmailAddress\n kind\n additionalEmailAddresses\n }\n }\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "867aad1276c26bd787ddf80c6f166abe";
|
||||
|
||||
export default node;
|
||||
@@ -0,0 +1,84 @@
|
||||
import z from "zod";
|
||||
import { peopleRoles } from "@probo/helpers";
|
||||
import { useOutletContext } from "react-router";
|
||||
import type { PeopleGraphNodeQuery$data } from "/hooks/graph/__generated__/PeopleGraphNodeQuery.graphql";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { useFormWithSchema } from "/hooks/useFormWithSchema";
|
||||
import { useMutationWithToasts } from "/hooks/useMutationWithToasts";
|
||||
import type { PeopleGraphUpdateMutation } from "/hooks/graph/__generated__/PeopleGraphUpdateMutation.graphql";
|
||||
import { updatePeopleMutation } from "/hooks/graph/PeopleGraph";
|
||||
import { Button, Card, Field } from "@probo/ui";
|
||||
import { EmailsField } from "/components/form/EmailsField";
|
||||
|
||||
const schema = z.object({
|
||||
fullName: z.string().min(1),
|
||||
primaryEmailAddress: z.string().email(),
|
||||
additionalEmailAddresses: z.preprocess(
|
||||
// Empty additional emails are skipped
|
||||
(v) => (v as string[]).filter((v) => !!v),
|
||||
z.array(z.string().email())
|
||||
),
|
||||
kind: z.enum(peopleRoles),
|
||||
});
|
||||
|
||||
export default function PeopleProfileTab() {
|
||||
const { people } = useOutletContext<{
|
||||
people: PeopleGraphNodeQuery$data["node"];
|
||||
}>();
|
||||
const { __ } = useTranslate();
|
||||
const { control, formState, handleSubmit, register, reset } =
|
||||
useFormWithSchema(schema, {
|
||||
defaultValues: {
|
||||
kind: people.kind,
|
||||
fullName: people.fullName,
|
||||
primaryEmailAddress: people.primaryEmailAddress,
|
||||
additionalEmailAddresses: [...(people.additionalEmailAddresses ?? [])],
|
||||
},
|
||||
});
|
||||
const [mutate, isMutating] = useMutationWithToasts<PeopleGraphUpdateMutation>(
|
||||
updatePeopleMutation,
|
||||
{
|
||||
successMessage: __("Member updated successfully."),
|
||||
errorMessage: __("Failed to update member. Please try again."),
|
||||
}
|
||||
);
|
||||
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
mutate({
|
||||
variables: {
|
||||
input: {
|
||||
id: people.id!,
|
||||
fullName: data.fullName,
|
||||
primaryEmailAddress: data.primaryEmailAddress,
|
||||
additionalEmailAddresses: data.additionalEmailAddresses,
|
||||
// TODO : make these field optional in the query (server side)
|
||||
kind: people.kind,
|
||||
},
|
||||
},
|
||||
onCompleted: () => {
|
||||
reset(data);
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return (
|
||||
<form onSubmit={onSubmit} className="space-y-4">
|
||||
<Card padded className="space-y-4">
|
||||
<Field label={__("Full name")} {...register("fullName")} type="text" />
|
||||
<Field
|
||||
label={__("Primary email")}
|
||||
{...register("primaryEmailAddress")}
|
||||
type="email"
|
||||
/>
|
||||
<EmailsField control={control} register={register} />
|
||||
</Card>
|
||||
<div className="flex justify-end">
|
||||
{formState.isDirty && (
|
||||
<Button type="submit" disabled={isMutating}>
|
||||
{__("Update")}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { Button, Card, IconCheckmark1 } from "@probo/ui";
|
||||
import type { PropsWithChildren } from "react";
|
||||
import z from "zod";
|
||||
import { useFormWithSchema } from "/hooks/useFormWithSchema";
|
||||
import { ControlledField } from "/components/form/ControlledField";
|
||||
import { Option } from "@probo/ui";
|
||||
import { getRoles, peopleRoles } from "@probo/helpers";
|
||||
import type { PeopleGraphNodeQuery$data } from "/hooks/graph/__generated__/PeopleGraphNodeQuery.graphql";
|
||||
import { useOutletContext } from "react-router";
|
||||
import { updatePeopleMutation } from "/hooks/graph/PeopleGraph";
|
||||
import { useMutationWithToasts } from "/hooks/useMutationWithToasts";
|
||||
import type { PeopleGraphUpdateMutation } from "/hooks/graph/__generated__/PeopleGraphUpdateMutation.graphql";
|
||||
|
||||
const schema = z.object({
|
||||
kind: z.enum(peopleRoles),
|
||||
});
|
||||
|
||||
export default function PeopleRoleTab() {
|
||||
const { people } = useOutletContext<{
|
||||
people: PeopleGraphNodeQuery$data["node"];
|
||||
}>();
|
||||
const { __ } = useTranslate();
|
||||
const { control, formState, handleSubmit, reset } = useFormWithSchema(
|
||||
schema,
|
||||
{
|
||||
defaultValues: {
|
||||
kind: people.kind,
|
||||
},
|
||||
}
|
||||
);
|
||||
const [mutate, isMutating] = useMutationWithToasts<PeopleGraphUpdateMutation>(
|
||||
updatePeopleMutation,
|
||||
{
|
||||
successMessage: __("Member updated successfully."),
|
||||
errorMessage: __("Failed to update member. Please try again."),
|
||||
}
|
||||
);
|
||||
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
mutate({
|
||||
variables: {
|
||||
input: {
|
||||
id: people.id!,
|
||||
kind: data.kind,
|
||||
// TODO : make these field optional in the query (server side)
|
||||
fullName: people.fullName!,
|
||||
primaryEmailAddress: people.primaryEmailAddress!,
|
||||
additionalEmailAddresses: people.additionalEmailAddresses ?? [],
|
||||
},
|
||||
},
|
||||
onCompleted: () => {
|
||||
reset(data);
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return (
|
||||
<form onSubmit={onSubmit} className="space-y-4">
|
||||
<Card padded className="space-y-4">
|
||||
<ControlledField
|
||||
control={control}
|
||||
name="kind"
|
||||
type="select"
|
||||
label={__("Role")}
|
||||
>
|
||||
{getRoles(__).map((role) => (
|
||||
<Option key={role.value} value={role.value}>
|
||||
{role.label}
|
||||
</Option>
|
||||
))}
|
||||
</ControlledField>
|
||||
<div className="space-y-2 ">
|
||||
<div className="text-sm font-medium">{__("Permissions")}</div>
|
||||
<ul className="text-sm text-txt-tertiary space-y-2">
|
||||
<AccessItem>
|
||||
{__("Access dashboard & reports relevant to their team")}
|
||||
</AccessItem>
|
||||
<AccessItem>
|
||||
{__("Create and manage own tasks, tickets, or projects")}
|
||||
</AccessItem>
|
||||
<AccessItem>
|
||||
{__("Comment on shared documents or projects")}
|
||||
</AccessItem>
|
||||
<AccessItem>
|
||||
{__("Receive notifications and system alerts")}
|
||||
</AccessItem>
|
||||
<AccessItem>
|
||||
{__("Join and participate in team chats or threads")}
|
||||
</AccessItem>
|
||||
</ul>
|
||||
</div>
|
||||
</Card>
|
||||
<div className="flex justify-end">
|
||||
{formState.isDirty && (
|
||||
<Button type="submit" disabled={isMutating}>
|
||||
{__("Update")}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
function AccessItem({ children }: PropsWithChildren) {
|
||||
return (
|
||||
<li className="flex gap-2 items-center">
|
||||
<IconCheckmark1 size={16} />
|
||||
{children}
|
||||
</li>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export default function PeopleTasksTab() {
|
||||
return (
|
||||
<div className="text-sm text-txt-tertiary text-center">
|
||||
Not available yet
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -9,10 +9,10 @@ import {
|
||||
Td,
|
||||
Avatar,
|
||||
Badge,
|
||||
ConfirmDialog,
|
||||
IconTrashCan,
|
||||
Button,
|
||||
IconPlusLarge,
|
||||
useConfirm,
|
||||
} from "@probo/ui";
|
||||
import {
|
||||
useFragment,
|
||||
@@ -151,18 +151,30 @@ function PolicyRow({
|
||||
const signedCount = signatures.filter(
|
||||
(signature) => signature.state === "SIGNED"
|
||||
).length;
|
||||
const [deletePolicy] = useDeletePolicyMutation();
|
||||
const [deletePolicy, isDeleting] = useDeletePolicyMutation();
|
||||
const confirm = useConfirm();
|
||||
|
||||
const handleDelete = () => {
|
||||
return new Promise<void>((resolve) => {
|
||||
deletePolicy({
|
||||
variables: {
|
||||
input: { policyId: policy.id },
|
||||
connections: [connectionId],
|
||||
},
|
||||
onCompleted: () => resolve(),
|
||||
});
|
||||
});
|
||||
confirm(
|
||||
() =>
|
||||
new Promise<void>((resolve) => {
|
||||
deletePolicy({
|
||||
variables: {
|
||||
input: { policyId: policy.id },
|
||||
connections: [connectionId],
|
||||
},
|
||||
onCompleted: () => resolve(),
|
||||
});
|
||||
}),
|
||||
{
|
||||
message: sprintf(
|
||||
__(
|
||||
'This will permanently delete the policy "%s". This action cannot be undone.'
|
||||
),
|
||||
policy.title
|
||||
),
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -203,17 +215,12 @@ function PolicyRow({
|
||||
{signedCount}/{signatures.length}
|
||||
</Td>
|
||||
<Td noLink width={50} className="text-end">
|
||||
<ConfirmDialog
|
||||
message={sprintf(
|
||||
__(
|
||||
'This will permanently delete the policy "%s". This action cannot be undone.'
|
||||
),
|
||||
policy.title
|
||||
)}
|
||||
onConfirm={handleDelete}
|
||||
>
|
||||
<Button icon={IconTrashCan} variant="danger" />
|
||||
</ConfirmDialog>
|
||||
<Button
|
||||
icon={IconTrashCan}
|
||||
variant="danger"
|
||||
onClick={handleDelete}
|
||||
disabled={isDeleting}
|
||||
/>
|
||||
</Td>
|
||||
</Tr>
|
||||
</>
|
||||
|
||||
@@ -25,13 +25,12 @@ import {
|
||||
Badge,
|
||||
Avatar,
|
||||
DropdownItem,
|
||||
ConfirmDialog,
|
||||
ActionDropdown,
|
||||
IconTrashCan,
|
||||
IconPencil,
|
||||
useConfirmDialogRef,
|
||||
IconClock,
|
||||
IconSignature,
|
||||
useConfirm,
|
||||
} from "@probo/ui";
|
||||
import { useOrganizationId } from "/hooks/useOrganizationId";
|
||||
import { Button } from "@probo/ui";
|
||||
@@ -136,27 +135,39 @@ export default function PolicyPage(props: Props) {
|
||||
});
|
||||
};
|
||||
|
||||
const confirm = useConfirm();
|
||||
|
||||
const handleDelete = () => {
|
||||
return new Promise<void>((resolve) => {
|
||||
const connectionId = ConnectionHandler.getConnectionID(
|
||||
organizationId,
|
||||
PoliciesConnectionKey
|
||||
);
|
||||
deletePolicy({
|
||||
variables: {
|
||||
input: { policyId: policy.id },
|
||||
connections: [connectionId],
|
||||
},
|
||||
onSuccess() {
|
||||
navigate(`/organizations/${organizationId}/policies`);
|
||||
},
|
||||
onError: () => resolve(),
|
||||
});
|
||||
});
|
||||
confirm(
|
||||
() =>
|
||||
new Promise<void>((resolve) => {
|
||||
const connectionId = ConnectionHandler.getConnectionID(
|
||||
organizationId,
|
||||
PoliciesConnectionKey
|
||||
);
|
||||
deletePolicy({
|
||||
variables: {
|
||||
input: { policyId: policy.id },
|
||||
connections: [connectionId],
|
||||
},
|
||||
onSuccess() {
|
||||
navigate(`/organizations/${organizationId}/policies`);
|
||||
},
|
||||
onError: () => resolve(),
|
||||
});
|
||||
}),
|
||||
{
|
||||
message: sprintf(
|
||||
__(
|
||||
'This will permanently delete the policy "%s". This action cannot be undone.'
|
||||
),
|
||||
policy.title
|
||||
),
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const updateDialogRef = useRef<{ open: () => void }>(null);
|
||||
const confirmRef = useConfirmDialogRef();
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -165,17 +176,6 @@ export default function PolicyPage(props: Props) {
|
||||
policy={policy}
|
||||
connectionId={versionConnectionId}
|
||||
/>
|
||||
|
||||
<ConfirmDialog
|
||||
ref={confirmRef}
|
||||
message={sprintf(
|
||||
__(
|
||||
'This will permanently delete the policy "%s". This action cannot be undone.'
|
||||
),
|
||||
policy.title
|
||||
)}
|
||||
onConfirm={handleDelete}
|
||||
/>
|
||||
<div className="space-y-6">
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<Breadcrumb
|
||||
@@ -221,7 +221,7 @@ export default function PolicyPage(props: Props) {
|
||||
variant="danger"
|
||||
icon={IconTrashCan}
|
||||
disabled={isDeleting}
|
||||
onClick={() => confirmRef.current?.open()}
|
||||
onClick={handleDelete}
|
||||
>
|
||||
{__("Delete")}
|
||||
</DropdownItem>
|
||||
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
Badge,
|
||||
Breadcrumb,
|
||||
Button,
|
||||
ConfirmDialog,
|
||||
Drawer,
|
||||
DropdownItem,
|
||||
IconPencil,
|
||||
@@ -13,7 +12,7 @@ import {
|
||||
PropertyRow,
|
||||
TabLink,
|
||||
Tabs,
|
||||
useConfirmDialogRef,
|
||||
useConfirm,
|
||||
} from "@probo/ui";
|
||||
import { Outlet, useNavigate, useParams } from "react-router";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
@@ -49,28 +48,38 @@ export default function RiskDetailPage(props: Props) {
|
||||
const [deleteRisk] = useDeleteRiskMutation();
|
||||
|
||||
usePageTitle(risk.name ?? "Risk detail");
|
||||
const confirm = useConfirm();
|
||||
|
||||
const onDelete = (riskId: string) => {
|
||||
const onDelete = () => {
|
||||
const connectionId = ConnectionHandler.getConnectionID(
|
||||
organizationId,
|
||||
RisksConnectionKey
|
||||
);
|
||||
return new Promise<void>((resolve) => {
|
||||
deleteRisk({
|
||||
variables: {
|
||||
input: { riskId },
|
||||
connections: [connectionId],
|
||||
},
|
||||
onSuccess() {
|
||||
navigate(`/organizations/${organizationId}/risks`);
|
||||
resolve();
|
||||
},
|
||||
});
|
||||
});
|
||||
confirm(
|
||||
() =>
|
||||
new Promise<void>((resolve) => {
|
||||
deleteRisk({
|
||||
variables: {
|
||||
input: { riskId },
|
||||
connections: [connectionId],
|
||||
},
|
||||
onSuccess() {
|
||||
navigate(`/organizations/${organizationId}/risks`);
|
||||
resolve();
|
||||
},
|
||||
});
|
||||
}),
|
||||
{
|
||||
message: sprintf(
|
||||
__(
|
||||
'This will permanently delete the risk "%s". This action cannot be undone.'
|
||||
),
|
||||
risk.name
|
||||
),
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const confirmRef = useConfirmDialogRef();
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Header */}
|
||||
@@ -99,7 +108,7 @@ export default function RiskDetailPage(props: Props) {
|
||||
<DropdownItem
|
||||
variant="danger"
|
||||
icon={IconTrashCan}
|
||||
onClick={() => confirmRef.current?.open()}
|
||||
onClick={onDelete}
|
||||
>
|
||||
{__("Delete")}
|
||||
</DropdownItem>
|
||||
@@ -107,17 +116,6 @@ export default function RiskDetailPage(props: Props) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ConfirmDialog
|
||||
ref={confirmRef}
|
||||
message={sprintf(
|
||||
__(
|
||||
'This will permanently delete the risk "%s". This action cannot be undone.'
|
||||
),
|
||||
risk.name
|
||||
)}
|
||||
onConfirm={() => onDelete(riskId)}
|
||||
/>
|
||||
|
||||
<PageHeader title={risk.name} />
|
||||
|
||||
<Tabs>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import {
|
||||
ActionDropdown,
|
||||
Button,
|
||||
ConfirmDialog,
|
||||
DropdownItem,
|
||||
IconPencil,
|
||||
IconPlusLarge,
|
||||
@@ -14,16 +13,15 @@ import {
|
||||
Th,
|
||||
Thead,
|
||||
Tr,
|
||||
useConfirmDialogRef,
|
||||
useConfirm,
|
||||
useDialogRef,
|
||||
} from "@probo/ui";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import FormRiskDialog from "./FormRiskDialog";
|
||||
import { useOrganizationId } from "/hooks/useOrganizationId";
|
||||
import { useState } from "react";
|
||||
import { usePageTitle } from "@probo/hooks";
|
||||
import { getTreatment, sprintf } from "@probo/helpers";
|
||||
import type { ItemOf, NodeOf } from "/types";
|
||||
import type { NodeOf } from "/types";
|
||||
import { useDeleteRiskMutation, useRisksQuery } from "/hooks/graph/RiskGraph";
|
||||
import { SortableTable, SortableTh } from "/components/SortableTable";
|
||||
import type { PreloadedQuery } from "react-relay";
|
||||
@@ -103,31 +101,32 @@ function RiskRow(props: RowProps) {
|
||||
const { __ } = useTranslate();
|
||||
const { risk, connectionId, organizationId } = props;
|
||||
const [deleteRisk] = useDeleteRiskMutation();
|
||||
const onDelete = (riskId: string) => {
|
||||
return new Promise<void>((resolve) => {
|
||||
deleteRisk({
|
||||
variables: {
|
||||
input: { riskId },
|
||||
connections: [connectionId],
|
||||
},
|
||||
onCompleted: () => resolve(),
|
||||
});
|
||||
});
|
||||
};
|
||||
const confirmRef = useConfirmDialogRef();
|
||||
const formDialogRef = useDialogRef();
|
||||
return (
|
||||
<>
|
||||
<ConfirmDialog
|
||||
ref={confirmRef}
|
||||
message={sprintf(
|
||||
const confirm = useConfirm();
|
||||
const onDelete = () => {
|
||||
confirm(
|
||||
() =>
|
||||
new Promise<void>((resolve) => {
|
||||
deleteRisk({
|
||||
variables: {
|
||||
input: { riskId: risk.id },
|
||||
connections: [connectionId],
|
||||
},
|
||||
onCompleted: () => resolve(),
|
||||
});
|
||||
}),
|
||||
{
|
||||
message: sprintf(
|
||||
__(
|
||||
'This will permanently delete the risk "%s". This action cannot be undone.'
|
||||
),
|
||||
risk.name
|
||||
)}
|
||||
onConfirm={() => onDelete(risk.id)}
|
||||
/>
|
||||
),
|
||||
}
|
||||
);
|
||||
};
|
||||
const formDialogRef = useDialogRef();
|
||||
return (
|
||||
<>
|
||||
<FormRiskDialog
|
||||
ref={formDialogRef}
|
||||
risk={risk}
|
||||
@@ -147,7 +146,7 @@ function RiskRow(props: RowProps) {
|
||||
<ActionDropdown>
|
||||
<DropdownItem
|
||||
icon={IconPencil}
|
||||
onClick={() => confirmRef.current?.open()}
|
||||
onClick={() => formDialogRef.current?.open()}
|
||||
>
|
||||
{__("Edit")}
|
||||
</DropdownItem>
|
||||
@@ -155,7 +154,7 @@ function RiskRow(props: RowProps) {
|
||||
<DropdownItem
|
||||
variant="danger"
|
||||
icon={IconTrashCan}
|
||||
onClick={() => confirmRef.current?.open()}
|
||||
onClick={onDelete}
|
||||
>
|
||||
{__("Delete")}
|
||||
</DropdownItem>
|
||||
|
||||
@@ -14,8 +14,7 @@ import {
|
||||
ActionDropdown,
|
||||
DropdownItem,
|
||||
IconTrashCan,
|
||||
ConfirmDialog,
|
||||
useConfirmDialogRef,
|
||||
useConfirm,
|
||||
} from "@probo/ui";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { usePageTitle } from "@probo/hooks";
|
||||
@@ -133,40 +132,40 @@ function VendorRow({
|
||||
: false;
|
||||
|
||||
const [deleteVendor] = useDeleteVendorMutation();
|
||||
const confirm = useConfirm();
|
||||
|
||||
const onDelete = (vendorId: string) => {
|
||||
return new Promise<void>((resolve) => {
|
||||
deleteVendor({
|
||||
variables: {
|
||||
input: {
|
||||
vendorId,
|
||||
},
|
||||
connections: [
|
||||
ConnectionHandler.getConnectionID(
|
||||
organizationId,
|
||||
"VendorsPage_vendors"
|
||||
),
|
||||
],
|
||||
},
|
||||
onCompleted: () => resolve(),
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const confirmRef = useConfirmDialogRef();
|
||||
|
||||
return (
|
||||
<>
|
||||
<ConfirmDialog
|
||||
ref={confirmRef}
|
||||
message={sprintf(
|
||||
const onDelete = () => {
|
||||
confirm(
|
||||
() =>
|
||||
new Promise<void>((resolve) => {
|
||||
deleteVendor({
|
||||
variables: {
|
||||
input: {
|
||||
vendorId: vendor.id,
|
||||
},
|
||||
connections: [
|
||||
ConnectionHandler.getConnectionID(
|
||||
organizationId,
|
||||
"VendorsPage_vendors"
|
||||
),
|
||||
],
|
||||
},
|
||||
onCompleted: () => resolve(),
|
||||
});
|
||||
}),
|
||||
{
|
||||
message: sprintf(
|
||||
__(
|
||||
'This will permanently delete the vendor "%s". This action cannot be undone.'
|
||||
),
|
||||
vendor.name
|
||||
)}
|
||||
onConfirm={() => onDelete(vendor.id)}
|
||||
/>
|
||||
),
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tr to={`/organizations/${organizationId}/vendors/${vendor.id}`}>
|
||||
<Td>
|
||||
<div className="flex gap-2 items-center">
|
||||
@@ -194,10 +193,10 @@ function VendorRow({
|
||||
{isExpired ? __("Late") : __("In progress")}
|
||||
</Badge>
|
||||
</Td>
|
||||
<Td noLink>
|
||||
<Td noLink width={50} className="text-end">
|
||||
<ActionDropdown>
|
||||
<DropdownItem
|
||||
onClick={() => confirmRef.current?.open()}
|
||||
onClick={onDelete}
|
||||
variant="danger"
|
||||
icon={IconTrashCan}
|
||||
>
|
||||
|
||||
@@ -30,7 +30,7 @@ const fetchRelay: FetchFunction = async (
|
||||
request,
|
||||
variables,
|
||||
_,
|
||||
uploadables,
|
||||
uploadables
|
||||
) => {
|
||||
const requestInit: RequestInit = {
|
||||
method: "POST",
|
||||
@@ -46,7 +46,7 @@ const fetchRelay: FetchFunction = async (
|
||||
operationName: request.name,
|
||||
query: request.text,
|
||||
variables: variables,
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
const uploadableMap: {
|
||||
@@ -80,7 +80,7 @@ const fetchRelay: FetchFunction = async (
|
||||
|
||||
const response = await fetch(
|
||||
import.meta.env.VITE_API_URL + "/api/console/v1/query",
|
||||
requestInit,
|
||||
requestInit
|
||||
);
|
||||
|
||||
if (response.status === 500) {
|
||||
@@ -100,8 +100,8 @@ const fetchRelay: FetchFunction = async (
|
||||
`Error fetching GraphQL query '${
|
||||
request.name
|
||||
}' with variables '${JSON.stringify(variables)}': ${JSON.stringify(
|
||||
json.errors,
|
||||
)}`,
|
||||
json.errors
|
||||
)}`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -122,10 +122,10 @@ export const relayEnvironment = new Environment({
|
||||
/**
|
||||
* Provider for relay with the probo environment
|
||||
*/
|
||||
export const RelayProvider = ({ children }: PropsWithChildren) => {
|
||||
export function RelayProvider({ children }: PropsWithChildren) {
|
||||
return (
|
||||
<RelayEnvironmentProvider environment={relayEnvironment}>
|
||||
{children}
|
||||
</RelayEnvironmentProvider>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
createBrowserRouter,
|
||||
Navigate,
|
||||
useLoaderData,
|
||||
useRouteError,
|
||||
type RouteObject,
|
||||
@@ -18,13 +19,13 @@ import { riskRoutes } from "./routes/riskRoutes.ts";
|
||||
import { measureRoutes } from "./routes/measureRoutes.ts";
|
||||
import { policiesRoutes } from "./routes/policiesRoutes.ts";
|
||||
import { organizationViewQuery } from "./hooks/graph/OrganizationGraph.ts";
|
||||
import { peopleRoutes } from "./routes/peopleRoutes.ts";
|
||||
|
||||
function ErrorBoundary() {
|
||||
const error = useRouteError();
|
||||
if (error instanceof UnAuthenticatedError) {
|
||||
return <div></div>;
|
||||
return <Navigate to="/auth/login" />;
|
||||
}
|
||||
console.log(error);
|
||||
return <div>error</div>;
|
||||
}
|
||||
|
||||
@@ -50,6 +51,7 @@ const routes = [
|
||||
path: "/",
|
||||
Component: CenteredLayout,
|
||||
fallback: CenteredLayoutSkeleton,
|
||||
ErrorBoundary: ErrorBoundary,
|
||||
children: [
|
||||
{
|
||||
path: "",
|
||||
@@ -87,6 +89,7 @@ const routes = [
|
||||
...riskRoutes,
|
||||
...measureRoutes,
|
||||
...policiesRoutes,
|
||||
...peopleRoutes,
|
||||
],
|
||||
},
|
||||
] satisfies AppRoute[];
|
||||
|
||||
48
apps/console2/src/routes/peopleRoutes.ts
Normal file
48
apps/console2/src/routes/peopleRoutes.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { lazy } from "react";
|
||||
import { loadQuery } from "react-relay";
|
||||
import type { AppRoute } from "/routes.tsx";
|
||||
import { relayEnvironment } from "/providers/RelayProviders";
|
||||
import { PageSkeleton } from "/components/skeletons/PageSkeleton.tsx";
|
||||
import {
|
||||
paginatedPeopleQuery,
|
||||
peopleNodeQuery,
|
||||
} from "/hooks/graph/PeopleGraph";
|
||||
|
||||
export const peopleRoutes = [
|
||||
{
|
||||
path: "people",
|
||||
fallback: PageSkeleton,
|
||||
queryLoader: ({ organizationId }) =>
|
||||
loadQuery(relayEnvironment, paginatedPeopleQuery, { organizationId }),
|
||||
Component: lazy(() => import("/pages/organizations/people/PeopleListPage")),
|
||||
},
|
||||
{
|
||||
path: "people/:peopleId",
|
||||
fallback: PageSkeleton,
|
||||
queryLoader: ({ peopleId }) =>
|
||||
loadQuery(relayEnvironment, peopleNodeQuery, { peopleId }),
|
||||
Component: lazy(
|
||||
() => import("/pages/organizations/people/PeopleDetailPage")
|
||||
),
|
||||
children: [
|
||||
{
|
||||
path: "tasks",
|
||||
Component: lazy(
|
||||
() => import("/pages/organizations/people/tabs/PeopleTasksTab")
|
||||
),
|
||||
},
|
||||
{
|
||||
path: "role",
|
||||
Component: lazy(
|
||||
() => import("/pages/organizations/people/tabs/PeopleRoleTab")
|
||||
),
|
||||
},
|
||||
{
|
||||
path: "profile",
|
||||
Component: lazy(
|
||||
() => import("/pages/organizations/people/tabs/PeopleProfileTab")
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
] satisfies AppRoute[];
|
||||
@@ -9,3 +9,4 @@ export {
|
||||
export { times, groupBy, isEmpty } from "./array";
|
||||
export { randomInt } from "./number";
|
||||
export { getMeasureStateLabel, measureStates } from "./measure";
|
||||
export { getRole, getRoles, peopleRoles } from "./people";
|
||||
|
||||
37
packages/helpers/src/people.ts
Normal file
37
packages/helpers/src/people.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
type Translator = (s: string) => string;
|
||||
|
||||
export const peopleRoles = [
|
||||
"EMPLOYEE",
|
||||
"CONTRACTOR",
|
||||
"SERVICE_ACCOUNT",
|
||||
] as const;
|
||||
|
||||
export function getRoles(__: Translator) {
|
||||
return [
|
||||
{
|
||||
value: "EMPLOYEE",
|
||||
label: __("Employee"),
|
||||
},
|
||||
{
|
||||
value: "CONTRACTOR",
|
||||
label: __("Contractor"),
|
||||
},
|
||||
{
|
||||
value: "SERVICE_ACCOUNT",
|
||||
label: __("Service account"),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export function getRole(__: Translator, role?: string): string {
|
||||
switch (role) {
|
||||
case "EMPLOYEE":
|
||||
return __("Employee");
|
||||
case "CONTRACTOR":
|
||||
return __("Contractor");
|
||||
case "SERVICE_ACCOUNT":
|
||||
return __("Service account");
|
||||
default:
|
||||
return __("Unknown");
|
||||
}
|
||||
}
|
||||
@@ -5,13 +5,13 @@ import { Outlet } from "react-router";
|
||||
export function AuthLayout() {
|
||||
const { __ } = useTranslate();
|
||||
return (
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 min-h-screen">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 min-h-screen text-txt-primary">
|
||||
<div className="bg-level-0 flex flex-col items-center justify-center">
|
||||
<div className="max-w-112">
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
<div className="hidden lg:flex bg-dialog text-invert text-5xl font-bold flex flex-col items-center justify-center p-8 text-balance lg:p-10">
|
||||
<div className="hidden lg:flex bg-dialog text-invert text-5xl font-bold flex flex-col items-center justify-center p-8 text-txt-primary lg:p-10">
|
||||
<div className="flex flex-col 2xl:flex-row-reverse items-center justify-center gap-4">
|
||||
<img src={logo} alt="Probo logo" className="h-auto w-96" />
|
||||
<span>
|
||||
|
||||
@@ -12,6 +12,7 @@ import { Logo } from "../Atoms/Logo/Logo.tsx";
|
||||
import { Toasts } from "../Atoms/Toasts/Toasts.tsx";
|
||||
import { createPortal } from "react-dom";
|
||||
import clsx from "clsx";
|
||||
import { ConfirmDialog } from "../Molecules/Dialog/ConfirmDialog.tsx";
|
||||
|
||||
type Props = PropsWithChildren<{
|
||||
header: ReactNode;
|
||||
@@ -62,6 +63,7 @@ export function Layout({ header, sidebar, children }: Props) {
|
||||
</main>
|
||||
</div>
|
||||
<Toasts />
|
||||
<ConfirmDialog />
|
||||
</div>
|
||||
</LayoutContext>
|
||||
);
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
Cancel,
|
||||
} from "@radix-ui/react-alert-dialog";
|
||||
import {
|
||||
useCallback,
|
||||
useRef,
|
||||
useState,
|
||||
type ComponentProps,
|
||||
@@ -18,57 +19,94 @@ import {
|
||||
import { Button } from "../../Atoms/Button/Button";
|
||||
import { Root as Portal } from "@radix-ui/react-portal";
|
||||
import { dialog } from "./Dialog";
|
||||
import { create } from "zustand";
|
||||
import { combine } from "zustand/middleware";
|
||||
|
||||
type Props = {
|
||||
children?: ReactNode;
|
||||
title?: ReactNode;
|
||||
message: string;
|
||||
type State = {
|
||||
title?: string;
|
||||
message: string | null;
|
||||
variant?: ComponentProps<typeof Button>["variant"];
|
||||
label?: string;
|
||||
onConfirm?: () => Promise<void>;
|
||||
ref?: RefObject<{ open: () => void } | null>;
|
||||
onConfirm: () => Promise<void>;
|
||||
};
|
||||
|
||||
export const useConfirmDialogRef = () => {
|
||||
return useRef<{ open: () => void } | null>(null);
|
||||
};
|
||||
const useConfirmStore = create(
|
||||
combine(
|
||||
{
|
||||
message: null,
|
||||
onConfirm: () => Promise.resolve(),
|
||||
} as State,
|
||||
(set) => ({
|
||||
open: (props: State) => {
|
||||
set(props);
|
||||
},
|
||||
close: () => {
|
||||
set({
|
||||
message: null,
|
||||
});
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
export function ConfirmDialog(props: Props) {
|
||||
/**
|
||||
* Hook used to open a confirm dialog
|
||||
*/
|
||||
export function useConfirm() {
|
||||
const open = useConfirmStore((state) => state.open);
|
||||
const { __ } = useTranslate();
|
||||
const title = props.title ?? __("Are you sure ?");
|
||||
const variant = props.variant ?? "danger";
|
||||
const label = variant === "danger" ? __("Delete") : props.label;
|
||||
const [open, setOpen] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
return useCallback(
|
||||
(cb: State["onConfirm"], props: Omit<State, "onConfirm">) => {
|
||||
open({
|
||||
onConfirm: cb,
|
||||
...props,
|
||||
message: props.message,
|
||||
title: props.title ?? __("Are you sure ?"),
|
||||
variant: props.variant ?? "danger",
|
||||
label: props.label ?? __("Delete"),
|
||||
});
|
||||
},
|
||||
[open],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Global component that displays a dialog when confirm() is called
|
||||
*/
|
||||
export function ConfirmDialog() {
|
||||
const { message, title, variant, label, onConfirm, close } =
|
||||
useConfirmStore();
|
||||
const { __ } = useTranslate();
|
||||
const isOpen = !!message;
|
||||
const {
|
||||
overlay,
|
||||
content,
|
||||
header,
|
||||
footer,
|
||||
title: titleClassname,
|
||||
footer,
|
||||
} = dialog();
|
||||
const onConfirm = () => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const handleConfirm = () => {
|
||||
setLoading(true);
|
||||
props.onConfirm?.().then(() => {
|
||||
setLoading(false);
|
||||
setOpen(false);
|
||||
});
|
||||
onConfirm()
|
||||
.then(() => {
|
||||
close();
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
if (props.ref) {
|
||||
props.ref.current = {
|
||||
open: () => setOpen(true),
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<Root open={open} onOpenChange={setOpen}>
|
||||
{props.children && <Trigger asChild children={props.children} />}
|
||||
<Root open={isOpen} onOpenChange={close}>
|
||||
<Portal>
|
||||
<Overlay className={overlay()} />
|
||||
<Content className={content({ className: "max-w-[500px]" })}>
|
||||
<header className={header()}>
|
||||
<Title children={title} className={titleClassname()} />
|
||||
</header>
|
||||
<Description className="p-6" children={props.message} />
|
||||
<Description className="p-6" children={message} />
|
||||
<footer className={footer()}>
|
||||
<Cancel asChild>
|
||||
<Button disabled={loading} variant="tertiary">
|
||||
@@ -78,7 +116,7 @@ export function ConfirmDialog(props: Props) {
|
||||
<Button
|
||||
disabled={loading}
|
||||
variant={variant}
|
||||
onClick={onConfirm}
|
||||
onClick={handleConfirm}
|
||||
>
|
||||
{label}
|
||||
</Button>
|
||||
|
||||
@@ -47,13 +47,10 @@ export {
|
||||
DialogTitle,
|
||||
useDialogRef,
|
||||
} from "./Molecules/Dialog/Dialog";
|
||||
export { useConfirm } from "./Molecules/Dialog/ConfirmDialog";
|
||||
export { RiskBadge } from "./Molecules/Badge/RiskBadge";
|
||||
export { SeverityBadge } from "./Molecules/Badge/SeverityBadge.tsx";
|
||||
export { PolicyVersionBadge } from "./Molecules/Badge/PolicyVersionBadge.tsx";
|
||||
export {
|
||||
ConfirmDialog,
|
||||
useConfirmDialogRef,
|
||||
} from "./Molecules/Dialog/ConfirmDialog.tsx";
|
||||
export { RisksChart } from "./Molecules/Risks/RisksChart";
|
||||
export { RiskOverview } from "./Molecules/Risks/RiskOverview";
|
||||
export { Combobox, ComboboxItem } from "./Molecules/Combobox/Combobox";
|
||||
|
||||
Reference in New Issue
Block a user