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 { graphql } from "relay-runtime";
|
||||||
import type { PeopleGraphQuery } from "./__generated__/PeopleGraphQuery.graphql";
|
import type { PeopleGraphQuery } from "./__generated__/PeopleGraphQuery.graphql";
|
||||||
import { useLazyLoadQuery } from "react-relay";
|
import {
|
||||||
import { useMemo } from "react";
|
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`
|
const peopleQuery = graphql`
|
||||||
query PeopleGraphQuery($organizationId: ID!) {
|
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) {
|
export function usePeople(organizationId: string) {
|
||||||
const data = useLazyLoadQuery<PeopleGraphQuery>(peopleQuery, {
|
const data = useLazyLoadQuery<PeopleGraphQuery>(peopleQuery, {
|
||||||
organizationId: organizationId,
|
organizationId: organizationId,
|
||||||
@@ -29,3 +45,139 @@ export function usePeople(organizationId: string) {
|
|||||||
return data.organization?.peoples?.edges.map((edge) => edge.node) ?? [];
|
return data.organization?.peoples?.edges.map((edge) => edge.node) ?? [];
|
||||||
}, [data]);
|
}, [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,
|
ActionDropdown,
|
||||||
Button,
|
Button,
|
||||||
Card,
|
Card,
|
||||||
ConfirmDialog,
|
|
||||||
DropdownItem,
|
DropdownItem,
|
||||||
FileButton,
|
FileButton,
|
||||||
IconChevronDown,
|
IconChevronDown,
|
||||||
@@ -27,7 +26,7 @@ import {
|
|||||||
Th,
|
Th,
|
||||||
Thead,
|
Thead,
|
||||||
Tr,
|
Tr,
|
||||||
useConfirmDialogRef,
|
useConfirm,
|
||||||
} from "@probo/ui";
|
} from "@probo/ui";
|
||||||
import {
|
import {
|
||||||
measuresQuery,
|
measuresQuery,
|
||||||
@@ -245,9 +244,12 @@ type MeasureRowProps = {
|
|||||||
function MeasureRow(props: MeasureRowProps) {
|
function MeasureRow(props: MeasureRowProps) {
|
||||||
const { __ } = useTranslate();
|
const { __ } = useTranslate();
|
||||||
const [deleteMeasure, isDeleting] = useDeleteMeasureMutation();
|
const [deleteMeasure, isDeleting] = useDeleteMeasureMutation();
|
||||||
|
const confirm = useConfirm();
|
||||||
|
|
||||||
const onDelete = () => {
|
const onDelete = () => {
|
||||||
return new Promise<void>((resolve) => {
|
confirm(
|
||||||
|
() =>
|
||||||
|
new Promise<void>((resolve) => {
|
||||||
deleteMeasure({
|
deleteMeasure({
|
||||||
variables: {
|
variables: {
|
||||||
input: { measureId: props.measure.id },
|
input: { measureId: props.measure.id },
|
||||||
@@ -255,23 +257,20 @@ function MeasureRow(props: MeasureRowProps) {
|
|||||||
},
|
},
|
||||||
onCompleted: () => resolve(),
|
onCompleted: () => resolve(),
|
||||||
});
|
});
|
||||||
});
|
}),
|
||||||
};
|
{
|
||||||
|
message: sprintf(
|
||||||
const confirmRef = useConfirmDialogRef();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<ConfirmDialog
|
|
||||||
message={sprintf(
|
|
||||||
__(
|
__(
|
||||||
'This will permanently delete the measure "%s". This action cannot be undone.'
|
'This will permanently delete the measure "%s". This action cannot be undone.'
|
||||||
),
|
),
|
||||||
props.measure.name
|
props.measure.name
|
||||||
)}
|
),
|
||||||
onConfirm={onDelete}
|
}
|
||||||
ref={confirmRef}
|
);
|
||||||
/>
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
<Tr>
|
<Tr>
|
||||||
<Td>{props.measure.name}</Td>
|
<Td>{props.measure.name}</Td>
|
||||||
<Td width={120}>
|
<Td width={120}>
|
||||||
@@ -281,7 +280,7 @@ function MeasureRow(props: MeasureRowProps) {
|
|||||||
<ActionDropdown>
|
<ActionDropdown>
|
||||||
<DropdownItem icon={IconPencil}>{__("Edit")}</DropdownItem>
|
<DropdownItem icon={IconPencil}>{__("Edit")}</DropdownItem>
|
||||||
<DropdownItem
|
<DropdownItem
|
||||||
onClick={() => confirmRef.current?.open()}
|
onClick={onDelete}
|
||||||
disabled={isDeleting}
|
disabled={isDeleting}
|
||||||
variant="danger"
|
variant="danger"
|
||||||
icon={IconTrashCan}
|
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,
|
Td,
|
||||||
Avatar,
|
Avatar,
|
||||||
Badge,
|
Badge,
|
||||||
ConfirmDialog,
|
|
||||||
IconTrashCan,
|
IconTrashCan,
|
||||||
Button,
|
Button,
|
||||||
IconPlusLarge,
|
IconPlusLarge,
|
||||||
|
useConfirm,
|
||||||
} from "@probo/ui";
|
} from "@probo/ui";
|
||||||
import {
|
import {
|
||||||
useFragment,
|
useFragment,
|
||||||
@@ -151,10 +151,13 @@ function PolicyRow({
|
|||||||
const signedCount = signatures.filter(
|
const signedCount = signatures.filter(
|
||||||
(signature) => signature.state === "SIGNED"
|
(signature) => signature.state === "SIGNED"
|
||||||
).length;
|
).length;
|
||||||
const [deletePolicy] = useDeletePolicyMutation();
|
const [deletePolicy, isDeleting] = useDeletePolicyMutation();
|
||||||
|
const confirm = useConfirm();
|
||||||
|
|
||||||
const handleDelete = () => {
|
const handleDelete = () => {
|
||||||
return new Promise<void>((resolve) => {
|
confirm(
|
||||||
|
() =>
|
||||||
|
new Promise<void>((resolve) => {
|
||||||
deletePolicy({
|
deletePolicy({
|
||||||
variables: {
|
variables: {
|
||||||
input: { policyId: policy.id },
|
input: { policyId: policy.id },
|
||||||
@@ -162,7 +165,16 @@ function PolicyRow({
|
|||||||
},
|
},
|
||||||
onCompleted: () => resolve(),
|
onCompleted: () => resolve(),
|
||||||
});
|
});
|
||||||
});
|
}),
|
||||||
|
{
|
||||||
|
message: sprintf(
|
||||||
|
__(
|
||||||
|
'This will permanently delete the policy "%s". This action cannot be undone.'
|
||||||
|
),
|
||||||
|
policy.title
|
||||||
|
),
|
||||||
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -203,17 +215,12 @@ function PolicyRow({
|
|||||||
{signedCount}/{signatures.length}
|
{signedCount}/{signatures.length}
|
||||||
</Td>
|
</Td>
|
||||||
<Td noLink width={50} className="text-end">
|
<Td noLink width={50} className="text-end">
|
||||||
<ConfirmDialog
|
<Button
|
||||||
message={sprintf(
|
icon={IconTrashCan}
|
||||||
__(
|
variant="danger"
|
||||||
'This will permanently delete the policy "%s". This action cannot be undone.'
|
onClick={handleDelete}
|
||||||
),
|
disabled={isDeleting}
|
||||||
policy.title
|
/>
|
||||||
)}
|
|
||||||
onConfirm={handleDelete}
|
|
||||||
>
|
|
||||||
<Button icon={IconTrashCan} variant="danger" />
|
|
||||||
</ConfirmDialog>
|
|
||||||
</Td>
|
</Td>
|
||||||
</Tr>
|
</Tr>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -25,13 +25,12 @@ import {
|
|||||||
Badge,
|
Badge,
|
||||||
Avatar,
|
Avatar,
|
||||||
DropdownItem,
|
DropdownItem,
|
||||||
ConfirmDialog,
|
|
||||||
ActionDropdown,
|
ActionDropdown,
|
||||||
IconTrashCan,
|
IconTrashCan,
|
||||||
IconPencil,
|
IconPencil,
|
||||||
useConfirmDialogRef,
|
|
||||||
IconClock,
|
IconClock,
|
||||||
IconSignature,
|
IconSignature,
|
||||||
|
useConfirm,
|
||||||
} from "@probo/ui";
|
} from "@probo/ui";
|
||||||
import { useOrganizationId } from "/hooks/useOrganizationId";
|
import { useOrganizationId } from "/hooks/useOrganizationId";
|
||||||
import { Button } from "@probo/ui";
|
import { Button } from "@probo/ui";
|
||||||
@@ -136,8 +135,12 @@ export default function PolicyPage(props: Props) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const confirm = useConfirm();
|
||||||
|
|
||||||
const handleDelete = () => {
|
const handleDelete = () => {
|
||||||
return new Promise<void>((resolve) => {
|
confirm(
|
||||||
|
() =>
|
||||||
|
new Promise<void>((resolve) => {
|
||||||
const connectionId = ConnectionHandler.getConnectionID(
|
const connectionId = ConnectionHandler.getConnectionID(
|
||||||
organizationId,
|
organizationId,
|
||||||
PoliciesConnectionKey
|
PoliciesConnectionKey
|
||||||
@@ -152,11 +155,19 @@ export default function PolicyPage(props: Props) {
|
|||||||
},
|
},
|
||||||
onError: () => resolve(),
|
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 updateDialogRef = useRef<{ open: () => void }>(null);
|
||||||
const confirmRef = useConfirmDialogRef();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -165,17 +176,6 @@ export default function PolicyPage(props: Props) {
|
|||||||
policy={policy}
|
policy={policy}
|
||||||
connectionId={versionConnectionId}
|
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="space-y-6">
|
||||||
<div className="flex justify-between items-center mb-4">
|
<div className="flex justify-between items-center mb-4">
|
||||||
<Breadcrumb
|
<Breadcrumb
|
||||||
@@ -221,7 +221,7 @@ export default function PolicyPage(props: Props) {
|
|||||||
variant="danger"
|
variant="danger"
|
||||||
icon={IconTrashCan}
|
icon={IconTrashCan}
|
||||||
disabled={isDeleting}
|
disabled={isDeleting}
|
||||||
onClick={() => confirmRef.current?.open()}
|
onClick={handleDelete}
|
||||||
>
|
>
|
||||||
{__("Delete")}
|
{__("Delete")}
|
||||||
</DropdownItem>
|
</DropdownItem>
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import {
|
|||||||
Badge,
|
Badge,
|
||||||
Breadcrumb,
|
Breadcrumb,
|
||||||
Button,
|
Button,
|
||||||
ConfirmDialog,
|
|
||||||
Drawer,
|
Drawer,
|
||||||
DropdownItem,
|
DropdownItem,
|
||||||
IconPencil,
|
IconPencil,
|
||||||
@@ -13,7 +12,7 @@ import {
|
|||||||
PropertyRow,
|
PropertyRow,
|
||||||
TabLink,
|
TabLink,
|
||||||
Tabs,
|
Tabs,
|
||||||
useConfirmDialogRef,
|
useConfirm,
|
||||||
} from "@probo/ui";
|
} from "@probo/ui";
|
||||||
import { Outlet, useNavigate, useParams } from "react-router";
|
import { Outlet, useNavigate, useParams } from "react-router";
|
||||||
import { useTranslate } from "@probo/i18n";
|
import { useTranslate } from "@probo/i18n";
|
||||||
@@ -49,13 +48,16 @@ export default function RiskDetailPage(props: Props) {
|
|||||||
const [deleteRisk] = useDeleteRiskMutation();
|
const [deleteRisk] = useDeleteRiskMutation();
|
||||||
|
|
||||||
usePageTitle(risk.name ?? "Risk detail");
|
usePageTitle(risk.name ?? "Risk detail");
|
||||||
|
const confirm = useConfirm();
|
||||||
|
|
||||||
const onDelete = (riskId: string) => {
|
const onDelete = () => {
|
||||||
const connectionId = ConnectionHandler.getConnectionID(
|
const connectionId = ConnectionHandler.getConnectionID(
|
||||||
organizationId,
|
organizationId,
|
||||||
RisksConnectionKey
|
RisksConnectionKey
|
||||||
);
|
);
|
||||||
return new Promise<void>((resolve) => {
|
confirm(
|
||||||
|
() =>
|
||||||
|
new Promise<void>((resolve) => {
|
||||||
deleteRisk({
|
deleteRisk({
|
||||||
variables: {
|
variables: {
|
||||||
input: { riskId },
|
input: { riskId },
|
||||||
@@ -66,11 +68,18 @@ export default function RiskDetailPage(props: Props) {
|
|||||||
resolve();
|
resolve();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
}),
|
||||||
|
{
|
||||||
|
message: sprintf(
|
||||||
|
__(
|
||||||
|
'This will permanently delete the risk "%s". This action cannot be undone.'
|
||||||
|
),
|
||||||
|
risk.name
|
||||||
|
),
|
||||||
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const confirmRef = useConfirmDialogRef();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
@@ -99,7 +108,7 @@ export default function RiskDetailPage(props: Props) {
|
|||||||
<DropdownItem
|
<DropdownItem
|
||||||
variant="danger"
|
variant="danger"
|
||||||
icon={IconTrashCan}
|
icon={IconTrashCan}
|
||||||
onClick={() => confirmRef.current?.open()}
|
onClick={onDelete}
|
||||||
>
|
>
|
||||||
{__("Delete")}
|
{__("Delete")}
|
||||||
</DropdownItem>
|
</DropdownItem>
|
||||||
@@ -107,17 +116,6 @@ export default function RiskDetailPage(props: Props) {
|
|||||||
</div>
|
</div>
|
||||||
</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} />
|
<PageHeader title={risk.name} />
|
||||||
|
|
||||||
<Tabs>
|
<Tabs>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
ActionDropdown,
|
ActionDropdown,
|
||||||
Button,
|
Button,
|
||||||
ConfirmDialog,
|
|
||||||
DropdownItem,
|
DropdownItem,
|
||||||
IconPencil,
|
IconPencil,
|
||||||
IconPlusLarge,
|
IconPlusLarge,
|
||||||
@@ -14,16 +13,15 @@ import {
|
|||||||
Th,
|
Th,
|
||||||
Thead,
|
Thead,
|
||||||
Tr,
|
Tr,
|
||||||
useConfirmDialogRef,
|
useConfirm,
|
||||||
useDialogRef,
|
useDialogRef,
|
||||||
} from "@probo/ui";
|
} from "@probo/ui";
|
||||||
import { useTranslate } from "@probo/i18n";
|
import { useTranslate } from "@probo/i18n";
|
||||||
import FormRiskDialog from "./FormRiskDialog";
|
import FormRiskDialog from "./FormRiskDialog";
|
||||||
import { useOrganizationId } from "/hooks/useOrganizationId";
|
import { useOrganizationId } from "/hooks/useOrganizationId";
|
||||||
import { useState } from "react";
|
|
||||||
import { usePageTitle } from "@probo/hooks";
|
import { usePageTitle } from "@probo/hooks";
|
||||||
import { getTreatment, sprintf } from "@probo/helpers";
|
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 { useDeleteRiskMutation, useRisksQuery } from "/hooks/graph/RiskGraph";
|
||||||
import { SortableTable, SortableTh } from "/components/SortableTable";
|
import { SortableTable, SortableTh } from "/components/SortableTable";
|
||||||
import type { PreloadedQuery } from "react-relay";
|
import type { PreloadedQuery } from "react-relay";
|
||||||
@@ -103,31 +101,32 @@ function RiskRow(props: RowProps) {
|
|||||||
const { __ } = useTranslate();
|
const { __ } = useTranslate();
|
||||||
const { risk, connectionId, organizationId } = props;
|
const { risk, connectionId, organizationId } = props;
|
||||||
const [deleteRisk] = useDeleteRiskMutation();
|
const [deleteRisk] = useDeleteRiskMutation();
|
||||||
const onDelete = (riskId: string) => {
|
const confirm = useConfirm();
|
||||||
return new Promise<void>((resolve) => {
|
const onDelete = () => {
|
||||||
|
confirm(
|
||||||
|
() =>
|
||||||
|
new Promise<void>((resolve) => {
|
||||||
deleteRisk({
|
deleteRisk({
|
||||||
variables: {
|
variables: {
|
||||||
input: { riskId },
|
input: { riskId: risk.id },
|
||||||
connections: [connectionId],
|
connections: [connectionId],
|
||||||
},
|
},
|
||||||
onCompleted: () => resolve(),
|
onCompleted: () => resolve(),
|
||||||
});
|
});
|
||||||
});
|
}),
|
||||||
};
|
{
|
||||||
const confirmRef = useConfirmDialogRef();
|
message: sprintf(
|
||||||
const formDialogRef = useDialogRef();
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<ConfirmDialog
|
|
||||||
ref={confirmRef}
|
|
||||||
message={sprintf(
|
|
||||||
__(
|
__(
|
||||||
'This will permanently delete the risk "%s". This action cannot be undone.'
|
'This will permanently delete the risk "%s". This action cannot be undone.'
|
||||||
),
|
),
|
||||||
risk.name
|
risk.name
|
||||||
)}
|
),
|
||||||
onConfirm={() => onDelete(risk.id)}
|
}
|
||||||
/>
|
);
|
||||||
|
};
|
||||||
|
const formDialogRef = useDialogRef();
|
||||||
|
return (
|
||||||
|
<>
|
||||||
<FormRiskDialog
|
<FormRiskDialog
|
||||||
ref={formDialogRef}
|
ref={formDialogRef}
|
||||||
risk={risk}
|
risk={risk}
|
||||||
@@ -147,7 +146,7 @@ function RiskRow(props: RowProps) {
|
|||||||
<ActionDropdown>
|
<ActionDropdown>
|
||||||
<DropdownItem
|
<DropdownItem
|
||||||
icon={IconPencil}
|
icon={IconPencil}
|
||||||
onClick={() => confirmRef.current?.open()}
|
onClick={() => formDialogRef.current?.open()}
|
||||||
>
|
>
|
||||||
{__("Edit")}
|
{__("Edit")}
|
||||||
</DropdownItem>
|
</DropdownItem>
|
||||||
@@ -155,7 +154,7 @@ function RiskRow(props: RowProps) {
|
|||||||
<DropdownItem
|
<DropdownItem
|
||||||
variant="danger"
|
variant="danger"
|
||||||
icon={IconTrashCan}
|
icon={IconTrashCan}
|
||||||
onClick={() => confirmRef.current?.open()}
|
onClick={onDelete}
|
||||||
>
|
>
|
||||||
{__("Delete")}
|
{__("Delete")}
|
||||||
</DropdownItem>
|
</DropdownItem>
|
||||||
|
|||||||
@@ -14,8 +14,7 @@ import {
|
|||||||
ActionDropdown,
|
ActionDropdown,
|
||||||
DropdownItem,
|
DropdownItem,
|
||||||
IconTrashCan,
|
IconTrashCan,
|
||||||
ConfirmDialog,
|
useConfirm,
|
||||||
useConfirmDialogRef,
|
|
||||||
} from "@probo/ui";
|
} from "@probo/ui";
|
||||||
import { useTranslate } from "@probo/i18n";
|
import { useTranslate } from "@probo/i18n";
|
||||||
import { usePageTitle } from "@probo/hooks";
|
import { usePageTitle } from "@probo/hooks";
|
||||||
@@ -133,13 +132,16 @@ function VendorRow({
|
|||||||
: false;
|
: false;
|
||||||
|
|
||||||
const [deleteVendor] = useDeleteVendorMutation();
|
const [deleteVendor] = useDeleteVendorMutation();
|
||||||
|
const confirm = useConfirm();
|
||||||
|
|
||||||
const onDelete = (vendorId: string) => {
|
const onDelete = () => {
|
||||||
return new Promise<void>((resolve) => {
|
confirm(
|
||||||
|
() =>
|
||||||
|
new Promise<void>((resolve) => {
|
||||||
deleteVendor({
|
deleteVendor({
|
||||||
variables: {
|
variables: {
|
||||||
input: {
|
input: {
|
||||||
vendorId,
|
vendorId: vendor.id,
|
||||||
},
|
},
|
||||||
connections: [
|
connections: [
|
||||||
ConnectionHandler.getConnectionID(
|
ConnectionHandler.getConnectionID(
|
||||||
@@ -150,23 +152,20 @@ function VendorRow({
|
|||||||
},
|
},
|
||||||
onCompleted: () => resolve(),
|
onCompleted: () => resolve(),
|
||||||
});
|
});
|
||||||
});
|
}),
|
||||||
};
|
{
|
||||||
|
message: sprintf(
|
||||||
const confirmRef = useConfirmDialogRef();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<ConfirmDialog
|
|
||||||
ref={confirmRef}
|
|
||||||
message={sprintf(
|
|
||||||
__(
|
__(
|
||||||
'This will permanently delete the vendor "%s". This action cannot be undone.'
|
'This will permanently delete the vendor "%s". This action cannot be undone.'
|
||||||
),
|
),
|
||||||
vendor.name
|
vendor.name
|
||||||
)}
|
),
|
||||||
onConfirm={() => onDelete(vendor.id)}
|
}
|
||||||
/>
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
<Tr to={`/organizations/${organizationId}/vendors/${vendor.id}`}>
|
<Tr to={`/organizations/${organizationId}/vendors/${vendor.id}`}>
|
||||||
<Td>
|
<Td>
|
||||||
<div className="flex gap-2 items-center">
|
<div className="flex gap-2 items-center">
|
||||||
@@ -194,10 +193,10 @@ function VendorRow({
|
|||||||
{isExpired ? __("Late") : __("In progress")}
|
{isExpired ? __("Late") : __("In progress")}
|
||||||
</Badge>
|
</Badge>
|
||||||
</Td>
|
</Td>
|
||||||
<Td noLink>
|
<Td noLink width={50} className="text-end">
|
||||||
<ActionDropdown>
|
<ActionDropdown>
|
||||||
<DropdownItem
|
<DropdownItem
|
||||||
onClick={() => confirmRef.current?.open()}
|
onClick={onDelete}
|
||||||
variant="danger"
|
variant="danger"
|
||||||
icon={IconTrashCan}
|
icon={IconTrashCan}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ const fetchRelay: FetchFunction = async (
|
|||||||
request,
|
request,
|
||||||
variables,
|
variables,
|
||||||
_,
|
_,
|
||||||
uploadables,
|
uploadables
|
||||||
) => {
|
) => {
|
||||||
const requestInit: RequestInit = {
|
const requestInit: RequestInit = {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@@ -46,7 +46,7 @@ const fetchRelay: FetchFunction = async (
|
|||||||
operationName: request.name,
|
operationName: request.name,
|
||||||
query: request.text,
|
query: request.text,
|
||||||
variables: variables,
|
variables: variables,
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const uploadableMap: {
|
const uploadableMap: {
|
||||||
@@ -80,7 +80,7 @@ const fetchRelay: FetchFunction = async (
|
|||||||
|
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
import.meta.env.VITE_API_URL + "/api/console/v1/query",
|
import.meta.env.VITE_API_URL + "/api/console/v1/query",
|
||||||
requestInit,
|
requestInit
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.status === 500) {
|
if (response.status === 500) {
|
||||||
@@ -100,8 +100,8 @@ const fetchRelay: FetchFunction = async (
|
|||||||
`Error fetching GraphQL query '${
|
`Error fetching GraphQL query '${
|
||||||
request.name
|
request.name
|
||||||
}' with variables '${JSON.stringify(variables)}': ${JSON.stringify(
|
}' 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
|
* Provider for relay with the probo environment
|
||||||
*/
|
*/
|
||||||
export const RelayProvider = ({ children }: PropsWithChildren) => {
|
export function RelayProvider({ children }: PropsWithChildren) {
|
||||||
return (
|
return (
|
||||||
<RelayEnvironmentProvider environment={relayEnvironment}>
|
<RelayEnvironmentProvider environment={relayEnvironment}>
|
||||||
{children}
|
{children}
|
||||||
</RelayEnvironmentProvider>
|
</RelayEnvironmentProvider>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
createBrowserRouter,
|
createBrowserRouter,
|
||||||
|
Navigate,
|
||||||
useLoaderData,
|
useLoaderData,
|
||||||
useRouteError,
|
useRouteError,
|
||||||
type RouteObject,
|
type RouteObject,
|
||||||
@@ -18,13 +19,13 @@ import { riskRoutes } from "./routes/riskRoutes.ts";
|
|||||||
import { measureRoutes } from "./routes/measureRoutes.ts";
|
import { measureRoutes } from "./routes/measureRoutes.ts";
|
||||||
import { policiesRoutes } from "./routes/policiesRoutes.ts";
|
import { policiesRoutes } from "./routes/policiesRoutes.ts";
|
||||||
import { organizationViewQuery } from "./hooks/graph/OrganizationGraph.ts";
|
import { organizationViewQuery } from "./hooks/graph/OrganizationGraph.ts";
|
||||||
|
import { peopleRoutes } from "./routes/peopleRoutes.ts";
|
||||||
|
|
||||||
function ErrorBoundary() {
|
function ErrorBoundary() {
|
||||||
const error = useRouteError();
|
const error = useRouteError();
|
||||||
if (error instanceof UnAuthenticatedError) {
|
if (error instanceof UnAuthenticatedError) {
|
||||||
return <div></div>;
|
return <Navigate to="/auth/login" />;
|
||||||
}
|
}
|
||||||
console.log(error);
|
|
||||||
return <div>error</div>;
|
return <div>error</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,6 +51,7 @@ const routes = [
|
|||||||
path: "/",
|
path: "/",
|
||||||
Component: CenteredLayout,
|
Component: CenteredLayout,
|
||||||
fallback: CenteredLayoutSkeleton,
|
fallback: CenteredLayoutSkeleton,
|
||||||
|
ErrorBoundary: ErrorBoundary,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: "",
|
path: "",
|
||||||
@@ -87,6 +89,7 @@ const routes = [
|
|||||||
...riskRoutes,
|
...riskRoutes,
|
||||||
...measureRoutes,
|
...measureRoutes,
|
||||||
...policiesRoutes,
|
...policiesRoutes,
|
||||||
|
...peopleRoutes,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
] satisfies AppRoute[];
|
] 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 { times, groupBy, isEmpty } from "./array";
|
||||||
export { randomInt } from "./number";
|
export { randomInt } from "./number";
|
||||||
export { getMeasureStateLabel, measureStates } from "./measure";
|
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() {
|
export function AuthLayout() {
|
||||||
const { __ } = useTranslate();
|
const { __ } = useTranslate();
|
||||||
return (
|
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="bg-level-0 flex flex-col items-center justify-center">
|
||||||
<div className="max-w-112">
|
<div className="max-w-112">
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</div>
|
</div>
|
||||||
</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">
|
<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" />
|
<img src={logo} alt="Probo logo" className="h-auto w-96" />
|
||||||
<span>
|
<span>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { Logo } from "../Atoms/Logo/Logo.tsx";
|
|||||||
import { Toasts } from "../Atoms/Toasts/Toasts.tsx";
|
import { Toasts } from "../Atoms/Toasts/Toasts.tsx";
|
||||||
import { createPortal } from "react-dom";
|
import { createPortal } from "react-dom";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
|
import { ConfirmDialog } from "../Molecules/Dialog/ConfirmDialog.tsx";
|
||||||
|
|
||||||
type Props = PropsWithChildren<{
|
type Props = PropsWithChildren<{
|
||||||
header: ReactNode;
|
header: ReactNode;
|
||||||
@@ -62,6 +63,7 @@ export function Layout({ header, sidebar, children }: Props) {
|
|||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
<Toasts />
|
<Toasts />
|
||||||
|
<ConfirmDialog />
|
||||||
</div>
|
</div>
|
||||||
</LayoutContext>
|
</LayoutContext>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
Cancel,
|
Cancel,
|
||||||
} from "@radix-ui/react-alert-dialog";
|
} from "@radix-ui/react-alert-dialog";
|
||||||
import {
|
import {
|
||||||
|
useCallback,
|
||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
type ComponentProps,
|
type ComponentProps,
|
||||||
@@ -18,57 +19,94 @@ import {
|
|||||||
import { Button } from "../../Atoms/Button/Button";
|
import { Button } from "../../Atoms/Button/Button";
|
||||||
import { Root as Portal } from "@radix-ui/react-portal";
|
import { Root as Portal } from "@radix-ui/react-portal";
|
||||||
import { dialog } from "./Dialog";
|
import { dialog } from "./Dialog";
|
||||||
|
import { create } from "zustand";
|
||||||
|
import { combine } from "zustand/middleware";
|
||||||
|
|
||||||
type Props = {
|
type State = {
|
||||||
children?: ReactNode;
|
title?: string;
|
||||||
title?: ReactNode;
|
message: string | null;
|
||||||
message: string;
|
|
||||||
variant?: ComponentProps<typeof Button>["variant"];
|
variant?: ComponentProps<typeof Button>["variant"];
|
||||||
label?: string;
|
label?: string;
|
||||||
onConfirm?: () => Promise<void>;
|
onConfirm: () => Promise<void>;
|
||||||
ref?: RefObject<{ open: () => void } | null>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useConfirmDialogRef = () => {
|
const useConfirmStore = create(
|
||||||
return useRef<{ open: () => void } | null>(null);
|
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 { __ } = useTranslate();
|
||||||
const title = props.title ?? __("Are you sure ?");
|
|
||||||
const variant = props.variant ?? "danger";
|
return useCallback(
|
||||||
const label = variant === "danger" ? __("Delete") : props.label;
|
(cb: State["onConfirm"], props: Omit<State, "onConfirm">) => {
|
||||||
const [open, setOpen] = useState(false);
|
open({
|
||||||
const [loading, setLoading] = useState(false);
|
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 {
|
const {
|
||||||
overlay,
|
overlay,
|
||||||
content,
|
content,
|
||||||
header,
|
header,
|
||||||
footer,
|
|
||||||
title: titleClassname,
|
title: titleClassname,
|
||||||
|
footer,
|
||||||
} = dialog();
|
} = dialog();
|
||||||
const onConfirm = () => {
|
const [loading, setLoading] = useState(false);
|
||||||
|
const handleConfirm = () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
props.onConfirm?.().then(() => {
|
onConfirm()
|
||||||
|
.then(() => {
|
||||||
|
close();
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
setOpen(false);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
if (props.ref) {
|
|
||||||
props.ref.current = {
|
|
||||||
open: () => setOpen(true),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<Root open={open} onOpenChange={setOpen}>
|
<Root open={isOpen} onOpenChange={close}>
|
||||||
{props.children && <Trigger asChild children={props.children} />}
|
|
||||||
<Portal>
|
<Portal>
|
||||||
<Overlay className={overlay()} />
|
<Overlay className={overlay()} />
|
||||||
<Content className={content({ className: "max-w-[500px]" })}>
|
<Content className={content({ className: "max-w-[500px]" })}>
|
||||||
<header className={header()}>
|
<header className={header()}>
|
||||||
<Title children={title} className={titleClassname()} />
|
<Title children={title} className={titleClassname()} />
|
||||||
</header>
|
</header>
|
||||||
<Description className="p-6" children={props.message} />
|
<Description className="p-6" children={message} />
|
||||||
<footer className={footer()}>
|
<footer className={footer()}>
|
||||||
<Cancel asChild>
|
<Cancel asChild>
|
||||||
<Button disabled={loading} variant="tertiary">
|
<Button disabled={loading} variant="tertiary">
|
||||||
@@ -78,7 +116,7 @@ export function ConfirmDialog(props: Props) {
|
|||||||
<Button
|
<Button
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
variant={variant}
|
variant={variant}
|
||||||
onClick={onConfirm}
|
onClick={handleConfirm}
|
||||||
>
|
>
|
||||||
{label}
|
{label}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -47,13 +47,10 @@ export {
|
|||||||
DialogTitle,
|
DialogTitle,
|
||||||
useDialogRef,
|
useDialogRef,
|
||||||
} from "./Molecules/Dialog/Dialog";
|
} from "./Molecules/Dialog/Dialog";
|
||||||
|
export { useConfirm } from "./Molecules/Dialog/ConfirmDialog";
|
||||||
export { RiskBadge } from "./Molecules/Badge/RiskBadge";
|
export { RiskBadge } from "./Molecules/Badge/RiskBadge";
|
||||||
export { SeverityBadge } from "./Molecules/Badge/SeverityBadge.tsx";
|
export { SeverityBadge } from "./Molecules/Badge/SeverityBadge.tsx";
|
||||||
export { PolicyVersionBadge } from "./Molecules/Badge/PolicyVersionBadge.tsx";
|
export { PolicyVersionBadge } from "./Molecules/Badge/PolicyVersionBadge.tsx";
|
||||||
export {
|
|
||||||
ConfirmDialog,
|
|
||||||
useConfirmDialogRef,
|
|
||||||
} from "./Molecules/Dialog/ConfirmDialog.tsx";
|
|
||||||
export { RisksChart } from "./Molecules/Risks/RisksChart";
|
export { RisksChart } from "./Molecules/Risks/RisksChart";
|
||||||
export { RiskOverview } from "./Molecules/Risks/RiskOverview";
|
export { RiskOverview } from "./Molecules/Risks/RiskOverview";
|
||||||
export { Combobox, ComboboxItem } from "./Molecules/Combobox/Combobox";
|
export { Combobox, ComboboxItem } from "./Molecules/Combobox/Combobox";
|
||||||
|
|||||||
Reference in New Issue
Block a user