Add tasks panels
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
daaee07952
commit
93aeedf1e1
90
apps/console2/src/components/form/MeasureSelectField.tsx
Normal file
90
apps/console2/src/components/form/MeasureSelectField.tsx
Normal file
@@ -0,0 +1,90 @@
|
||||
import { Field, Option, Select } from "@probo/ui";
|
||||
import { Suspense, useMemo, useState, type ComponentProps } from "react";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { type Control, Controller } from "react-hook-form";
|
||||
import { usePaginatedMeasures } from "/hooks/graph/usePaginatedMeasures";
|
||||
|
||||
type Props = {
|
||||
organizationId: string;
|
||||
control: Control<any>;
|
||||
name: string;
|
||||
label?: string;
|
||||
error?: string;
|
||||
disabled?: boolean;
|
||||
} & ComponentProps<typeof Field>;
|
||||
|
||||
export function MeasureSelectField({
|
||||
organizationId,
|
||||
control,
|
||||
disabled,
|
||||
...props
|
||||
}: Props) {
|
||||
return (
|
||||
<Field {...props}>
|
||||
<Suspense
|
||||
fallback={<Select variant="editor" disabled placeholder="Loading..." />}
|
||||
>
|
||||
<MeasureSelectWithQuery
|
||||
organizationId={organizationId}
|
||||
control={control}
|
||||
name={props.name}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</Suspense>
|
||||
</Field>
|
||||
);
|
||||
}
|
||||
|
||||
function MeasureSelectWithQuery(
|
||||
props: Pick<Props, "organizationId" | "control" | "name" | "disabled">
|
||||
) {
|
||||
const { __ } = useTranslate();
|
||||
const { name, organizationId, control, disabled } = props;
|
||||
const { data } = usePaginatedMeasures(organizationId);
|
||||
const [search, setSearch] = useState("");
|
||||
const measures = useMemo(() => {
|
||||
return (
|
||||
data?.measures.edges
|
||||
?.filter(
|
||||
(edge) =>
|
||||
edge.node.name.toLowerCase().includes(search.toLowerCase()) ||
|
||||
edge.node.description?.toLowerCase().includes(search.toLowerCase())
|
||||
)
|
||||
.map((edge) => edge.node) ?? []
|
||||
);
|
||||
}, [data?.measures.edges, search]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Controller
|
||||
control={control}
|
||||
name={name}
|
||||
render={({ field }) => (
|
||||
<Select
|
||||
id={name}
|
||||
variant="editor"
|
||||
placeholder={__("Select an owner")}
|
||||
onValueChange={field.onChange}
|
||||
{...field}
|
||||
className="w-full"
|
||||
value={field.value ?? ""}
|
||||
onSearch={setSearch}
|
||||
searchValue={search}
|
||||
disabled={disabled}
|
||||
>
|
||||
{measures?.map((m) => (
|
||||
<Option key={m.id} value={m.id}>
|
||||
<div className="space-y-1 text-start min-w-0">
|
||||
<div className="max-w-75 ellipsis overflow-hidden whitespace-pre-wrap">
|
||||
{m.name}
|
||||
</div>
|
||||
<div className="text-sm text-txt-secondary">{m.category}</div>
|
||||
</div>
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Avatar, Field, Option, Select } from "@probo/ui";
|
||||
import { Suspense } from "react";
|
||||
import { Suspense, type ComponentProps } from "react";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { type Control, Controller } from "react-hook-form";
|
||||
import { usePeople } from "/hooks/graph/PeopleGraph.ts";
|
||||
@@ -10,7 +10,7 @@ type Props = {
|
||||
name: string;
|
||||
label?: string;
|
||||
error?: string;
|
||||
};
|
||||
} & ComponentProps<typeof Field>;
|
||||
|
||||
export function PeopleSelectField({
|
||||
organizationId,
|
||||
@@ -26,6 +26,7 @@ export function PeopleSelectField({
|
||||
organizationId={organizationId}
|
||||
control={control}
|
||||
name={props.name}
|
||||
disabled={props.disabled}
|
||||
/>
|
||||
</Suspense>
|
||||
</Field>
|
||||
@@ -33,7 +34,7 @@ export function PeopleSelectField({
|
||||
}
|
||||
|
||||
function PeopleSelectWithQuery(
|
||||
props: Pick<Props, "organizationId" | "control" | "name">
|
||||
props: Pick<Props, "organizationId" | "control" | "name" | "disabled">
|
||||
) {
|
||||
const { __ } = useTranslate();
|
||||
const { name, organizationId, control } = props;
|
||||
@@ -46,6 +47,7 @@ function PeopleSelectWithQuery(
|
||||
name={name}
|
||||
render={({ field }) => (
|
||||
<Select
|
||||
disabled={props.disabled}
|
||||
id={name}
|
||||
variant="editor"
|
||||
placeholder={__("Select an owner")}
|
||||
|
||||
@@ -15,52 +15,8 @@ import {
|
||||
} from "@probo/ui";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { Suspense, useMemo, useState, type ReactNode } from "react";
|
||||
import { graphql } from "relay-runtime";
|
||||
import { useLazyLoadQuery, usePaginationFragment } from "react-relay";
|
||||
import type { LinkedMeasuresDialogQuery } from "./__generated__/LinkedMeasuresDialogQuery.graphql";
|
||||
import { useOrganizationId } from "/hooks/useOrganizationId";
|
||||
import type { LinkedMeasuresDialogFragment$key } from "./__generated__/LinkedMeasuresDialogFragment.graphql";
|
||||
|
||||
const measuresQuery = graphql`
|
||||
query LinkedMeasuresDialogQuery($organizationId: ID!) {
|
||||
organization: node(id: $organizationId) {
|
||||
id
|
||||
... on Organization {
|
||||
...LinkedMeasuresDialogFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const measuresFragment = graphql`
|
||||
fragment LinkedMeasuresDialogFragment on Organization
|
||||
@refetchable(queryName: "LinkedMeasuresDialogQuery_fragment")
|
||||
@argumentDefinitions(
|
||||
first: { type: "Int", defaultValue: 20 }
|
||||
order: { type: "MeasureOrder", defaultValue: null }
|
||||
after: { type: "CursorKey", defaultValue: null }
|
||||
before: { type: "CursorKey", defaultValue: null }
|
||||
last: { type: "Int", defaultValue: null }
|
||||
) {
|
||||
measures(
|
||||
first: $first
|
||||
after: $after
|
||||
last: $last
|
||||
before: $before
|
||||
orderBy: $order
|
||||
) @connection(key: "LinkedMeasuresDialogQuery_measures") {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
name
|
||||
state
|
||||
description
|
||||
category
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
import { usePaginatedMeasures } from "/hooks/graph/usePaginatedMeasures";
|
||||
|
||||
type Props = {
|
||||
children: ReactNode;
|
||||
@@ -88,13 +44,8 @@ export function LinkedMeasureDialog({ children, ...props }: Props) {
|
||||
|
||||
function LinkedMeasuresDialogContent(props: Omit<Props, "children">) {
|
||||
const organizationId = useOrganizationId();
|
||||
const query = useLazyLoadQuery<LinkedMeasuresDialogQuery>(measuresQuery, {
|
||||
organizationId,
|
||||
});
|
||||
const { data, loadNext, hasNext, isLoadingNext } = usePaginationFragment(
|
||||
measuresFragment,
|
||||
query.organization as LinkedMeasuresDialogFragment$key
|
||||
);
|
||||
const { data, loadNext, hasNext, isLoadingNext } =
|
||||
usePaginatedMeasures(organizationId);
|
||||
const { __ } = useTranslate();
|
||||
const [search, setSearch] = useState("");
|
||||
const [category, setCategory] = useState<string | null>(null);
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
Field,
|
||||
useDialogRef,
|
||||
} from "@probo/ui";
|
||||
import type { PropsWithChildren } from "react";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { graphql } from "relay-runtime";
|
||||
import { useOrganizationId } from "/hooks/useOrganizationId";
|
||||
import { useMutationWithToasts } from "/hooks/useMutationWithToasts";
|
||||
import { z } from "zod";
|
||||
import { useFormWithSchema } from "/hooks/useFormWithSchema";
|
||||
|
||||
const inviteMutation = graphql`
|
||||
mutation InviteUserDialogMutation($input: InviteUserInput!) {
|
||||
inviteUser(input: $input) {
|
||||
success
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const schema = z.object({
|
||||
email: z.string().email(),
|
||||
fullName: z.string(),
|
||||
});
|
||||
|
||||
export function InviteUserDialog({ children }: PropsWithChildren) {
|
||||
const { __ } = useTranslate();
|
||||
const organizationId = useOrganizationId();
|
||||
const [inviteUser, isInviting] = useMutationWithToasts(inviteMutation, {
|
||||
successMessage: __("User invited successfully"),
|
||||
errorMessage: __("Failed to invite user"),
|
||||
});
|
||||
const { register, handleSubmit, formState } = useFormWithSchema(schema, {});
|
||||
|
||||
const dialogRef = useDialogRef();
|
||||
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
inviteUser({
|
||||
variables: {
|
||||
input: {
|
||||
organizationId,
|
||||
email: data.email,
|
||||
fullName: data.fullName,
|
||||
},
|
||||
},
|
||||
onSuccess: () => {
|
||||
dialogRef.current?.close();
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
title={__("Invite member")}
|
||||
trigger={children}
|
||||
className="max-w-lg"
|
||||
ref={dialogRef}
|
||||
>
|
||||
<form onSubmit={onSubmit}>
|
||||
<DialogContent padded className="space-y-4">
|
||||
<p className="text-txt-secondary text-sm">
|
||||
Send an invitation to join your workspace.
|
||||
</p>
|
||||
<Field
|
||||
type="email"
|
||||
label={__("Email")}
|
||||
placeholder={__("Email")}
|
||||
{...register("email")}
|
||||
error={formState.errors.email?.message}
|
||||
/>
|
||||
<Field
|
||||
type="text"
|
||||
label={__("Full name")}
|
||||
placeholder={__("Full name")}
|
||||
{...register("fullName")}
|
||||
error={formState.errors.fullName?.message}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogFooter>
|
||||
<Button type="submit" disabled={isInviting}>
|
||||
{__("Invite user")}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
94
apps/console2/src/components/organizations/__generated__/InviteUserDialogMutation.graphql.ts
generated
Normal file
94
apps/console2/src/components/organizations/__generated__/InviteUserDialogMutation.graphql.ts
generated
Normal file
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* @generated SignedSource<<1cf40085b2959d7ebb48ee4a90be41b1>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type InviteUserInput = {
|
||||
email: string;
|
||||
fullName: string;
|
||||
organizationId: string;
|
||||
};
|
||||
export type InviteUserDialogMutation$variables = {
|
||||
input: InviteUserInput;
|
||||
};
|
||||
export type InviteUserDialogMutation$data = {
|
||||
readonly inviteUser: {
|
||||
readonly success: boolean;
|
||||
};
|
||||
};
|
||||
export type InviteUserDialogMutation = {
|
||||
response: InviteUserDialogMutation$data;
|
||||
variables: InviteUserDialogMutation$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "input"
|
||||
}
|
||||
],
|
||||
v1 = [
|
||||
{
|
||||
"alias": null,
|
||||
"args": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "input",
|
||||
"variableName": "input"
|
||||
}
|
||||
],
|
||||
"concreteType": "InviteUserPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "inviteUser",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "success",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
];
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "InviteUserDialogMutation",
|
||||
"selections": (v1/*: any*/),
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "InviteUserDialogMutation",
|
||||
"selections": (v1/*: any*/)
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "aa667927b5e2cd0019a8457edc286181",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "InviteUserDialogMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation InviteUserDialogMutation(\n $input: InviteUserInput!\n) {\n inviteUser(input: $input) {\n success\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "de4c5b5208a2ff0953e9d15b844df842";
|
||||
|
||||
export default node;
|
||||
@@ -1,22 +1,19 @@
|
||||
import { graphql } from "relay-runtime";
|
||||
import {
|
||||
IconPlusLarge,
|
||||
Button,
|
||||
Tr,
|
||||
Td,
|
||||
Table,
|
||||
Thead,
|
||||
Tbody,
|
||||
Th,
|
||||
IconChevronDown,
|
||||
RiskBadge,
|
||||
IconTrashCan,
|
||||
RiskBadge,
|
||||
Table,
|
||||
Tbody,
|
||||
Td,
|
||||
Th,
|
||||
Thead,
|
||||
Tr,
|
||||
TrButton,
|
||||
} from "@probo/ui";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import type { LinkedRisksCardFragment$key } from "./__generated__/LinkedRisksCardFragment.graphql";
|
||||
import { useFragment } from "react-relay";
|
||||
import { useMemo, useState } from "react";
|
||||
import { sprintf } from "@probo/helpers";
|
||||
import { useOrganizationId } from "/hooks/useOrganizationId";
|
||||
import { LinkedRisksDialog } from "./LinkedRisksDialog.tsx";
|
||||
|
||||
@@ -58,11 +55,6 @@ type Props<Params> = {
|
||||
*/
|
||||
export function LinkedRisksCard<Params>(props: Props<Params>) {
|
||||
const { __ } = useTranslate();
|
||||
const [limit, setLimit] = useState<number | null>(4);
|
||||
const risks = useMemo(() => {
|
||||
return limit ? props.risks.slice(0, limit) : props.risks;
|
||||
}, [props.risks, limit]);
|
||||
const showMoreButton = limit !== null && props.risks.length > limit;
|
||||
|
||||
const onAttach = (riskId: string) => {
|
||||
props.onAttach({
|
||||
@@ -90,48 +82,37 @@ export function LinkedRisksCard<Params>(props: Props<Params>) {
|
||||
|
||||
return (
|
||||
<div className="space-y-4 relative">
|
||||
{risks.length > 0 ? (
|
||||
<Table>
|
||||
<Thead>
|
||||
<Table>
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>{__("Name")}</Th>
|
||||
<Th>{__("Inherent Risk")}</Th>
|
||||
<Th>{__("Residual Risk")}</Th>
|
||||
<Th></Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{props.risks.length === 0 && (
|
||||
<Tr>
|
||||
<Th>{__("Name")}</Th>
|
||||
<Th>{__("Inherent Risk")}</Th>
|
||||
<Th>{__("Residual Risk")}</Th>
|
||||
<Th></Th>
|
||||
<Td colSpan={4} className="text-center text-txt-secondary">
|
||||
{__("No risks linked")}
|
||||
</Td>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{risks.map((risk) => (
|
||||
<RiskRow key={risk.id} risk={risk} onClick={onDetach} />
|
||||
))}
|
||||
</Tbody>
|
||||
</Table>
|
||||
) : (
|
||||
<div className="text-center text-sm text-txt-secondary">
|
||||
{__("No risks linked")}
|
||||
</div>
|
||||
)}
|
||||
{showMoreButton && (
|
||||
<Button
|
||||
variant="tertiary"
|
||||
onClick={() => setLimit(null)}
|
||||
className="mt-3 mx-auto"
|
||||
icon={IconChevronDown}
|
||||
>
|
||||
{sprintf(__("Show %s more"), props.risks.length - limit)}
|
||||
</Button>
|
||||
)}
|
||||
<LinkedRisksDialog
|
||||
connectionId={props.connectionId}
|
||||
disabled={props.disabled}
|
||||
linkedRisks={props.risks}
|
||||
onLink={onAttach}
|
||||
onUnlink={onDetach}
|
||||
>
|
||||
<Button variant="secondary" icon={IconPlusLarge} className="ml-auto">
|
||||
{__("Link risk")}
|
||||
</Button>
|
||||
</LinkedRisksDialog>
|
||||
)}
|
||||
{props.risks.map((risk) => (
|
||||
<RiskRow key={risk.id} risk={risk} onClick={onDetach} />
|
||||
))}
|
||||
<LinkedRisksDialog
|
||||
connectionId={props.connectionId}
|
||||
disabled={props.disabled}
|
||||
linkedRisks={props.risks}
|
||||
onLink={onAttach}
|
||||
onUnlink={onDetach}
|
||||
>
|
||||
<TrButton colspan={4}>{__("Link risk")}</TrButton>
|
||||
</LinkedRisksDialog>
|
||||
</Tbody>
|
||||
</Table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
238
apps/console2/src/components/tasks/TaskFormDialog.tsx
Normal file
238
apps/console2/src/components/tasks/TaskFormDialog.tsx
Normal file
@@ -0,0 +1,238 @@
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
DurationPicker,
|
||||
Input,
|
||||
Label,
|
||||
PropertyRow,
|
||||
Textarea,
|
||||
useDialogRef,
|
||||
type DialogRef,
|
||||
} from "@probo/ui";
|
||||
import type { ReactNode } from "react";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { Breadcrumb } from "@probo/ui";
|
||||
import { graphql } from "relay-runtime";
|
||||
import { useFragment } from "react-relay";
|
||||
import { z } from "zod";
|
||||
import { useFormWithSchema } from "/hooks/useFormWithSchema";
|
||||
import { useMutationWithToasts } from "/hooks/useMutationWithToasts";
|
||||
import { useOrganizationId } from "/hooks/useOrganizationId";
|
||||
import { PeopleSelectField } from "/components/form/PeopleSelectField";
|
||||
import type { TaskFormDialogFragment$key } from "./__generated__/TaskFormDialogFragment.graphql";
|
||||
import { MeasureSelectField } from "/components/form/MeasureSelectField";
|
||||
import { Controller } from "react-hook-form";
|
||||
|
||||
const taskFragment = graphql`
|
||||
fragment TaskFormDialogFragment on Task {
|
||||
id
|
||||
description
|
||||
name
|
||||
state
|
||||
timeEstimate
|
||||
deadline
|
||||
assignedTo {
|
||||
id
|
||||
}
|
||||
measure {
|
||||
id
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const taskCreateMutation = graphql`
|
||||
mutation TaskFormDialogCreateMutation(
|
||||
$input: CreateTaskInput!
|
||||
$connections: [ID!]!
|
||||
) {
|
||||
createTask(input: $input) {
|
||||
taskEdge @prependEdge(connections: $connections) {
|
||||
node {
|
||||
...TaskFormDialogFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const taskUpdateMutation = graphql`
|
||||
mutation TaskFormDialogUpdateMutation($input: UpdateTaskInput!) {
|
||||
updateTask(input: $input) {
|
||||
task {
|
||||
...TaskFormDialogFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const schema = z.object({
|
||||
name: z.string(),
|
||||
description: z.string(),
|
||||
timeEstimate: z.string().nullable(),
|
||||
assignedToId: z.string(),
|
||||
measureId: z.string(),
|
||||
deadline: z.date({
|
||||
coerce: true,
|
||||
}),
|
||||
});
|
||||
|
||||
type Props = {
|
||||
children?: ReactNode;
|
||||
task?: TaskFormDialogFragment$key;
|
||||
connection?: string;
|
||||
ref?: DialogRef;
|
||||
measureId?: string;
|
||||
};
|
||||
|
||||
export default function TaskFormDialog(props: Props) {
|
||||
const { __ } = useTranslate();
|
||||
const dialogRef = props.ref ?? useDialogRef();
|
||||
const organizationId = useOrganizationId();
|
||||
const task = useFragment(taskFragment, props.task);
|
||||
const [mutate] = task
|
||||
? useMutationWithToasts(taskUpdateMutation, {
|
||||
successMessage: __("Task updated successfully."),
|
||||
errorMessage: __("Failed to update task. Please try again."),
|
||||
})
|
||||
: useMutationWithToasts(taskCreateMutation, {
|
||||
successMessage: __("Task created successfully."),
|
||||
errorMessage: __("Failed to create task. Please try again."),
|
||||
});
|
||||
|
||||
const { control, handleSubmit, register, formState } = useFormWithSchema(
|
||||
schema,
|
||||
{
|
||||
defaultValues: {
|
||||
name: task?.name ?? "",
|
||||
description: task?.description ?? "",
|
||||
timeEstimate: task?.timeEstimate ?? "",
|
||||
assignedToId: task?.assignedTo?.id ?? "",
|
||||
measureId: task?.measure?.id ?? props.measureId ?? "",
|
||||
deadline: task?.deadline.split("T")[0] ?? new Date(),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const onSubmit = handleSubmit(async (data) => {
|
||||
if (task) {
|
||||
await mutate({
|
||||
variables: {
|
||||
input: {
|
||||
taskId: task.id,
|
||||
name: data.name,
|
||||
description: data.description,
|
||||
timeEstimate: data.timeEstimate || null,
|
||||
deadline: data.deadline,
|
||||
},
|
||||
},
|
||||
});
|
||||
} else {
|
||||
await mutate({
|
||||
variables: {
|
||||
input: {
|
||||
organizationId,
|
||||
name: data.name,
|
||||
description: data.description,
|
||||
timeEstimate: data.timeEstimate || null,
|
||||
deadline: data.deadline,
|
||||
assignedToId: data.assignedToId,
|
||||
measureId: data.measureId,
|
||||
},
|
||||
connections: [props.connection!],
|
||||
},
|
||||
});
|
||||
}
|
||||
dialogRef.current?.close();
|
||||
});
|
||||
const isUpdating = !!task;
|
||||
const showMeasure = !props.measureId;
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
ref={dialogRef}
|
||||
trigger={props.children}
|
||||
title={
|
||||
<Breadcrumb
|
||||
items={[__("Tasks"), isUpdating ? __("Edit Task") : __("New Task")]}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<form onSubmit={onSubmit}>
|
||||
<DialogContent className="grid grid-cols-[1fr_420px]">
|
||||
<div className="py-8 px-10 space-y-4">
|
||||
<Input
|
||||
id="title"
|
||||
required
|
||||
variant="title"
|
||||
placeholder={__("Task title")}
|
||||
{...register("name")}
|
||||
/>
|
||||
<Textarea
|
||||
id="content"
|
||||
variant="ghost"
|
||||
autogrow
|
||||
placeholder={__("Add description")}
|
||||
{...register("description")}
|
||||
/>
|
||||
</div>
|
||||
{/* Properties form */}
|
||||
<div className="py-5 px-6 bg-subtle">
|
||||
<Label>{__("Properties")}</Label>
|
||||
<PropertyRow
|
||||
label={__("Assigned to")}
|
||||
error={formState.errors.assignedToId?.message}
|
||||
>
|
||||
<PeopleSelectField
|
||||
disabled={isUpdating}
|
||||
name="assignedToId"
|
||||
control={control}
|
||||
organizationId={organizationId}
|
||||
/>
|
||||
</PropertyRow>
|
||||
{showMeasure && (
|
||||
<PropertyRow
|
||||
label={__("Measure")}
|
||||
error={formState.errors.measureId?.message}
|
||||
>
|
||||
<MeasureSelectField
|
||||
disabled={isUpdating}
|
||||
name="measureId"
|
||||
control={control}
|
||||
organizationId={organizationId}
|
||||
/>
|
||||
</PropertyRow>
|
||||
)}
|
||||
<PropertyRow
|
||||
label={__("Time estimate")}
|
||||
error={formState.errors.timeEstimate?.message}
|
||||
>
|
||||
<Controller
|
||||
name="timeEstimate"
|
||||
control={control}
|
||||
render={({ field: { onChange, ...field } }) => (
|
||||
<DurationPicker
|
||||
{...field}
|
||||
onValueChange={(value) => onChange(value)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</PropertyRow>
|
||||
<PropertyRow
|
||||
label={__("Deadline")}
|
||||
error={formState.errors.deadline?.message}
|
||||
>
|
||||
<Input id="deadline" type="date" {...register("deadline")} />
|
||||
</PropertyRow>
|
||||
</div>
|
||||
</DialogContent>
|
||||
<DialogFooter>
|
||||
<Button type="submit">
|
||||
{isUpdating ? __("Update task") : __("Create task")}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
231
apps/console2/src/components/tasks/TasksCard.tsx
Normal file
231
apps/console2/src/components/tasks/TasksCard.tsx
Normal file
@@ -0,0 +1,231 @@
|
||||
import {
|
||||
ActionDropdown,
|
||||
Avatar,
|
||||
Card,
|
||||
DropdownItem,
|
||||
IconArrowCornerDownLeft,
|
||||
IconPencil,
|
||||
IconTrashCan,
|
||||
PriorityLevel,
|
||||
Spinner,
|
||||
TabBadge,
|
||||
TabItem,
|
||||
Tabs,
|
||||
TaskStateIcon,
|
||||
useConfirm,
|
||||
useDialogRef,
|
||||
} from "@probo/ui";
|
||||
import { graphql, useMutation } from "react-relay";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { usePageTitle } from "@probo/hooks";
|
||||
import type { ItemOf } from "/types";
|
||||
import TaskFormDialog, {
|
||||
taskUpdateMutation,
|
||||
} from "/components/tasks/TaskFormDialog";
|
||||
import { useOrganizationId } from "/hooks/useOrganizationId";
|
||||
import { Link, useLocation } from "react-router";
|
||||
import { promisifyMutation } from "@probo/helpers";
|
||||
import type { TaskFormDialogFragment$key } from "./__generated__/TaskFormDialogFragment.graphql";
|
||||
|
||||
type Props = {
|
||||
tasks: ({
|
||||
assignedTo?: {
|
||||
id: string;
|
||||
fullName: string;
|
||||
} | null;
|
||||
id: string;
|
||||
name: string;
|
||||
state: "TODO" | "DONE";
|
||||
description: string;
|
||||
measure?: {
|
||||
id: string;
|
||||
name: string;
|
||||
} | null;
|
||||
} & TaskFormDialogFragment$key)[];
|
||||
connectionId: string;
|
||||
};
|
||||
|
||||
export default function TasksCard({ tasks, connectionId }: Props) {
|
||||
const { __ } = useTranslate();
|
||||
const hash = useLocation().hash.replace("#", "");
|
||||
|
||||
const hashes = [
|
||||
{ hash: "", label: __("To do"), state: "TODO" },
|
||||
{ hash: "done", label: __("Done"), state: "DONE" },
|
||||
{ hash: "all", label: __("All"), state: null },
|
||||
] as const;
|
||||
|
||||
const tasksPerHash = new Map([
|
||||
["", tasks?.filter((t) => t.state === "TODO")],
|
||||
["done", tasks?.filter((t) => t.state === "DONE")],
|
||||
["all", tasks],
|
||||
]);
|
||||
|
||||
const filteredTasks = tasksPerHash.get(hash) ?? [];
|
||||
|
||||
usePageTitle(__("Tasks"));
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{tasks?.length === 0 ? (
|
||||
<p className="text-center py-6 text-txt-secondary">{__("No tasks")}</p>
|
||||
) : (
|
||||
<Card>
|
||||
<Tabs className="px-6">
|
||||
{hashes.map((h) => (
|
||||
<TabItem asChild active={hash === h.hash} key={h.hash}>
|
||||
<Link to={`#${h.hash}`}>
|
||||
{h.label}
|
||||
<TabBadge>{tasksPerHash.get(h.hash)?.length}</TabBadge>
|
||||
</Link>
|
||||
</TabItem>
|
||||
))}
|
||||
</Tabs>
|
||||
<div className="divide-y divide-border-solid">
|
||||
{hash === "all"
|
||||
? // All tabs group the todo using the state
|
||||
hashes
|
||||
.slice(0, 2)
|
||||
.filter((h) => tasksPerHash.get(h.hash)?.length)
|
||||
.map((h) => (
|
||||
<>
|
||||
<h2 className="px-6 py-3 text-sm font-medium flex items-center gap-2 bg-subtle">
|
||||
<TaskStateIcon state={h.state!} />
|
||||
{h.label}
|
||||
</h2>
|
||||
{tasksPerHash
|
||||
.get(h.hash)
|
||||
?.map((task) => (
|
||||
<TaskRow
|
||||
key={task.id}
|
||||
task={task}
|
||||
connectionId={connectionId}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
))
|
||||
: // Todo and Done tab simply list todos
|
||||
filteredTasks?.map((task) => (
|
||||
<TaskRow
|
||||
key={task.id}
|
||||
task={task}
|
||||
connectionId={connectionId}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type TaskRowProps = {
|
||||
task: ItemOf<Props["tasks"]> & TaskFormDialogFragment$key;
|
||||
connectionId: string;
|
||||
};
|
||||
|
||||
const deleteMutation = graphql`
|
||||
mutation TasksCardDeleteMutation(
|
||||
$input: DeleteTaskInput!
|
||||
$connections: [ID!]!
|
||||
) {
|
||||
deleteTask(input: $input) {
|
||||
deletedTaskId @deleteEdge(connections: $connections)
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
function TaskRow(props: TaskRowProps) {
|
||||
const organizationId = useOrganizationId();
|
||||
const dialogRef = useDialogRef();
|
||||
const { __ } = useTranslate();
|
||||
const confirm = useConfirm();
|
||||
const [deleteTask] = useMutation(deleteMutation);
|
||||
|
||||
const [updateTask, isUpdating] = useMutation(taskUpdateMutation);
|
||||
|
||||
const onToggle = () => {
|
||||
promisifyMutation(updateTask)({
|
||||
variables: {
|
||||
input: {
|
||||
taskId: props.task.id,
|
||||
state: props.task.state === "TODO" ? "DONE" : "TODO",
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const onDelete = () => {
|
||||
confirm(
|
||||
() =>
|
||||
promisifyMutation(deleteTask)({
|
||||
variables: {
|
||||
input: { taskId: props.task.id },
|
||||
connections: [props.connectionId],
|
||||
},
|
||||
}),
|
||||
{
|
||||
message: "Are you sure you want to delete this task?",
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<TaskFormDialog task={props.task} ref={dialogRef} />
|
||||
<div className="flex items-center justify-between py-3 px-6">
|
||||
<div className="flex gap-2 items-start">
|
||||
<div className="flex items-center gap-2 pt-[2px]">
|
||||
<PriorityLevel level={1} />
|
||||
<button
|
||||
onClick={onToggle}
|
||||
className="cursor-pointer -m-1 p-1 disabled:opacity-60"
|
||||
disabled={isUpdating}
|
||||
>
|
||||
<TaskStateIcon state={props.task.state} />
|
||||
</button>
|
||||
</div>
|
||||
<div className="text-sm space-y-1">
|
||||
<h2 className="font-medium">{props.task.name}</h2>
|
||||
{props.task.measure && (
|
||||
<p className="text-txt-secondary flex items-center gap-2">
|
||||
<IconArrowCornerDownLeft className="scale-x-[-1]" size={16} />
|
||||
<Link
|
||||
className="hover:underline"
|
||||
to={`/organizations/${organizationId}/measures/${props.task.measure?.id}`}
|
||||
>
|
||||
{props.task.measure?.name}
|
||||
</Link>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2 items-center">
|
||||
{isUpdating && <Spinner size={16} />}
|
||||
{props.task.assignedTo && (
|
||||
<Link
|
||||
to={`/organizations/${organizationId}/people/${props.task.assignedTo?.id}`}
|
||||
>
|
||||
<Avatar name={props.task.assignedTo?.fullName ?? ""} />
|
||||
</Link>
|
||||
)}
|
||||
<ActionDropdown>
|
||||
<DropdownItem
|
||||
icon={IconPencil}
|
||||
onClick={() => dialogRef.current?.open()}
|
||||
>
|
||||
{__("Edit")}
|
||||
</DropdownItem>
|
||||
<DropdownItem
|
||||
variant="danger"
|
||||
icon={IconTrashCan}
|
||||
onClick={onDelete}
|
||||
>
|
||||
{__("Delete")}
|
||||
</DropdownItem>
|
||||
</ActionDropdown>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
249
apps/console2/src/components/tasks/__generated__/TaskFormDialogCreateMutation.graphql.ts
generated
Normal file
249
apps/console2/src/components/tasks/__generated__/TaskFormDialogCreateMutation.graphql.ts
generated
Normal file
@@ -0,0 +1,249 @@
|
||||
/**
|
||||
* @generated SignedSource<<beed7398f44a7093301b72d7bceeeba1>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
import { FragmentRefs } from "relay-runtime";
|
||||
export type CreateTaskInput = {
|
||||
assignedToId?: string | null | undefined;
|
||||
deadline?: any | null | undefined;
|
||||
description: string;
|
||||
measureId?: string | null | undefined;
|
||||
name: string;
|
||||
organizationId: string;
|
||||
timeEstimate?: any | null | undefined;
|
||||
};
|
||||
export type TaskFormDialogCreateMutation$variables = {
|
||||
connections: ReadonlyArray<string>;
|
||||
input: CreateTaskInput;
|
||||
};
|
||||
export type TaskFormDialogCreateMutation$data = {
|
||||
readonly createTask: {
|
||||
readonly taskEdge: {
|
||||
readonly node: {
|
||||
readonly " $fragmentSpreads": FragmentRefs<"TaskFormDialogFragment">;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
export type TaskFormDialogCreateMutation = {
|
||||
response: TaskFormDialogCreateMutation$data;
|
||||
variables: TaskFormDialogCreateMutation$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": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
v4 = [
|
||||
(v3/*: any*/)
|
||||
];
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": [
|
||||
(v0/*: any*/),
|
||||
(v1/*: any*/)
|
||||
],
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "TaskFormDialogCreateMutation",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v2/*: any*/),
|
||||
"concreteType": "CreateTaskPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "createTask",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "TaskEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "taskEdge",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Task",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"args": null,
|
||||
"kind": "FragmentSpread",
|
||||
"name": "TaskFormDialogFragment"
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": [
|
||||
(v1/*: any*/),
|
||||
(v0/*: any*/)
|
||||
],
|
||||
"kind": "Operation",
|
||||
"name": "TaskFormDialogCreateMutation",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v2/*: any*/),
|
||||
"concreteType": "CreateTaskPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "createTask",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "TaskEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "taskEdge",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Task",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "description",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "name",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "state",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "timeEstimate",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "deadline",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "People",
|
||||
"kind": "LinkedField",
|
||||
"name": "assignedTo",
|
||||
"plural": false,
|
||||
"selections": (v4/*: any*/),
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Measure",
|
||||
"kind": "LinkedField",
|
||||
"name": "measure",
|
||||
"plural": false,
|
||||
"selections": (v4/*: any*/),
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"filters": null,
|
||||
"handle": "prependEdge",
|
||||
"key": "",
|
||||
"kind": "LinkedHandle",
|
||||
"name": "taskEdge",
|
||||
"handleArgs": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "connections",
|
||||
"variableName": "connections"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "f605aed8a6f83a622d32e9b42f52f524",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "TaskFormDialogCreateMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation TaskFormDialogCreateMutation(\n $input: CreateTaskInput!\n) {\n createTask(input: $input) {\n taskEdge {\n node {\n ...TaskFormDialogFragment\n id\n }\n }\n }\n}\n\nfragment TaskFormDialogFragment on Task {\n id\n description\n name\n state\n timeEstimate\n deadline\n assignedTo {\n id\n }\n measure {\n id\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "3a5194da3b1d57be836ca5e3405b2c3a";
|
||||
|
||||
export default node;
|
||||
115
apps/console2/src/components/tasks/__generated__/TaskFormDialogFragment.graphql.ts
generated
Normal file
115
apps/console2/src/components/tasks/__generated__/TaskFormDialogFragment.graphql.ts
generated
Normal file
@@ -0,0 +1,115 @@
|
||||
/**
|
||||
* @generated SignedSource<<d6d04868c777f81982de3bffb9a7e73f>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ReaderFragment } from 'relay-runtime';
|
||||
export type TaskState = "DONE" | "TODO";
|
||||
import { FragmentRefs } from "relay-runtime";
|
||||
export type TaskFormDialogFragment$data = {
|
||||
readonly assignedTo: {
|
||||
readonly id: string;
|
||||
} | null | undefined;
|
||||
readonly deadline: any | null | undefined;
|
||||
readonly description: string;
|
||||
readonly id: string;
|
||||
readonly measure: {
|
||||
readonly id: string;
|
||||
} | null | undefined;
|
||||
readonly name: string;
|
||||
readonly state: TaskState;
|
||||
readonly timeEstimate: any | null | undefined;
|
||||
readonly " $fragmentType": "TaskFormDialogFragment";
|
||||
};
|
||||
export type TaskFormDialogFragment$key = {
|
||||
readonly " $data"?: TaskFormDialogFragment$data;
|
||||
readonly " $fragmentSpreads": FragmentRefs<"TaskFormDialogFragment">;
|
||||
};
|
||||
|
||||
const node: ReaderFragment = (function(){
|
||||
var v0 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
v1 = [
|
||||
(v0/*: any*/)
|
||||
];
|
||||
return {
|
||||
"argumentDefinitions": [],
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "TaskFormDialogFragment",
|
||||
"selections": [
|
||||
(v0/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "description",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "name",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "state",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "timeEstimate",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "deadline",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "People",
|
||||
"kind": "LinkedField",
|
||||
"name": "assignedTo",
|
||||
"plural": false,
|
||||
"selections": (v1/*: any*/),
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Measure",
|
||||
"kind": "LinkedField",
|
||||
"name": "measure",
|
||||
"plural": false,
|
||||
"selections": (v1/*: any*/),
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Task",
|
||||
"abstractKey": null
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "3a4bede2199df797a20a6d87358d41cf";
|
||||
|
||||
export default node;
|
||||
199
apps/console2/src/components/tasks/__generated__/TaskFormDialogUpdateMutation.graphql.ts
generated
Normal file
199
apps/console2/src/components/tasks/__generated__/TaskFormDialogUpdateMutation.graphql.ts
generated
Normal file
@@ -0,0 +1,199 @@
|
||||
/**
|
||||
* @generated SignedSource<<1cc9998f8dcbb02ac977744023d372d6>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
import { FragmentRefs } from "relay-runtime";
|
||||
export type TaskState = "DONE" | "TODO";
|
||||
export type UpdateTaskInput = {
|
||||
deadline?: any | null | undefined;
|
||||
description?: string | null | undefined;
|
||||
name?: string | null | undefined;
|
||||
state?: TaskState | null | undefined;
|
||||
taskId: string;
|
||||
timeEstimate?: any | null | undefined;
|
||||
};
|
||||
export type TaskFormDialogUpdateMutation$variables = {
|
||||
input: UpdateTaskInput;
|
||||
};
|
||||
export type TaskFormDialogUpdateMutation$data = {
|
||||
readonly updateTask: {
|
||||
readonly task: {
|
||||
readonly " $fragmentSpreads": FragmentRefs<"TaskFormDialogFragment">;
|
||||
};
|
||||
};
|
||||
};
|
||||
export type TaskFormDialogUpdateMutation = {
|
||||
response: TaskFormDialogUpdateMutation$data;
|
||||
variables: TaskFormDialogUpdateMutation$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "input"
|
||||
}
|
||||
],
|
||||
v1 = [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "input",
|
||||
"variableName": "input"
|
||||
}
|
||||
],
|
||||
v2 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
v3 = [
|
||||
(v2/*: any*/)
|
||||
];
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "TaskFormDialogUpdateMutation",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v1/*: any*/),
|
||||
"concreteType": "UpdateTaskPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "updateTask",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Task",
|
||||
"kind": "LinkedField",
|
||||
"name": "task",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"args": null,
|
||||
"kind": "FragmentSpread",
|
||||
"name": "TaskFormDialogFragment"
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "TaskFormDialogUpdateMutation",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v1/*: any*/),
|
||||
"concreteType": "UpdateTaskPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "updateTask",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Task",
|
||||
"kind": "LinkedField",
|
||||
"name": "task",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "description",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "name",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "state",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "timeEstimate",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "deadline",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "People",
|
||||
"kind": "LinkedField",
|
||||
"name": "assignedTo",
|
||||
"plural": false,
|
||||
"selections": (v3/*: any*/),
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Measure",
|
||||
"kind": "LinkedField",
|
||||
"name": "measure",
|
||||
"plural": false,
|
||||
"selections": (v3/*: any*/),
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "bfcbdf0470627b6f7b9a98a1722f7dca",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "TaskFormDialogUpdateMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation TaskFormDialogUpdateMutation(\n $input: UpdateTaskInput!\n) {\n updateTask(input: $input) {\n task {\n ...TaskFormDialogFragment\n id\n }\n }\n}\n\nfragment TaskFormDialogFragment on Task {\n id\n description\n name\n state\n timeEstimate\n deadline\n assignedTo {\n id\n }\n measure {\n id\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "7c174671b0235b09cfe0fa98d4b3e629";
|
||||
|
||||
export default node;
|
||||
132
apps/console2/src/components/tasks/__generated__/TasksCardDeleteMutation.graphql.ts
generated
Normal file
132
apps/console2/src/components/tasks/__generated__/TasksCardDeleteMutation.graphql.ts
generated
Normal file
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* @generated SignedSource<<2866738fc7aeb6abd91cf4ed4ae72727>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type DeleteTaskInput = {
|
||||
taskId: string;
|
||||
};
|
||||
export type TasksCardDeleteMutation$variables = {
|
||||
connections: ReadonlyArray<string>;
|
||||
input: DeleteTaskInput;
|
||||
};
|
||||
export type TasksCardDeleteMutation$data = {
|
||||
readonly deleteTask: {
|
||||
readonly deletedTaskId: string;
|
||||
};
|
||||
};
|
||||
export type TasksCardDeleteMutation = {
|
||||
response: TasksCardDeleteMutation$data;
|
||||
variables: TasksCardDeleteMutation$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": "deletedTaskId",
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": [
|
||||
(v0/*: any*/),
|
||||
(v1/*: any*/)
|
||||
],
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "TasksCardDeleteMutation",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v2/*: any*/),
|
||||
"concreteType": "DeleteTaskPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "deleteTask",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": [
|
||||
(v1/*: any*/),
|
||||
(v0/*: any*/)
|
||||
],
|
||||
"kind": "Operation",
|
||||
"name": "TasksCardDeleteMutation",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v2/*: any*/),
|
||||
"concreteType": "DeleteTaskPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "deleteTask",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"filters": null,
|
||||
"handle": "deleteEdge",
|
||||
"key": "",
|
||||
"kind": "ScalarHandle",
|
||||
"name": "deletedTaskId",
|
||||
"handleArgs": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "connections",
|
||||
"variableName": "connections"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "1ab085f9650841990644d2c106effac7",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "TasksCardDeleteMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation TasksCardDeleteMutation(\n $input: DeleteTaskInput!\n) {\n deleteTask(input: $input) {\n deletedTaskId\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "803ffe0cb54f7fa5c840f6d3e4f2592b";
|
||||
|
||||
export default node;
|
||||
@@ -47,6 +47,7 @@ export const measureNodeQuery = graphql`
|
||||
state
|
||||
category
|
||||
...MeasureRisksTabFragment
|
||||
...MeasureTasksTabFragment
|
||||
...MeasureControlsTabFragment
|
||||
...MeasureFormDialogMeasureFragment
|
||||
...MeasureEvidencesTabFragment
|
||||
|
||||
12
apps/console2/src/hooks/graph/TaskGraph.ts
Normal file
12
apps/console2/src/hooks/graph/TaskGraph.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { graphql } from "relay-runtime";
|
||||
|
||||
export const tasksQuery = graphql`
|
||||
query TaskGraphQuery($organizationId: ID!) {
|
||||
organization: node(id: $organizationId) {
|
||||
... on Organization {
|
||||
id
|
||||
...TasksPageFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<7258ea81187f177613d9ef146df494ff>>
|
||||
* @generated SignedSource<<5cb97bc6e333575149d6cc4418370d45>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -385,7 +385,8 @@ return {
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
},
|
||||
(v9/*: any*/)
|
||||
],
|
||||
"storageKey": "controls(first:100)"
|
||||
}
|
||||
@@ -399,12 +400,12 @@ return {
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "8071a760c42f4c3537bbc25d7b866419",
|
||||
"cacheID": "4b21f0ec7a74eed125d96e4cfc7f94fd",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "FrameworkGraphNodeQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query FrameworkGraphNodeQuery(\n $frameworkId: ID!\n) {\n node(id: $frameworkId) {\n __typename\n ... on Framework {\n id\n name\n ...FrameworkDetailPageFragment\n }\n id\n }\n}\n\nfragment FrameworkDetailPageFragment on Framework {\n id\n name\n description\n controls(first: 100) {\n edges {\n node {\n id\n sectionTitle\n name\n description\n measures(first: 100) {\n edges {\n node {\n id\n ...LinkedMeasuresCardFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n documents(first: 100) {\n edges {\n node {\n id\n ...LinkedDocumentsCardFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n }\n }\n}\n\nfragment LinkedDocumentsCardFragment on Document {\n id\n title\n createdAt\n documentType\n versions(first: 1) {\n edges {\n node {\n id\n status\n }\n }\n }\n}\n\nfragment LinkedMeasuresCardFragment on Measure {\n id\n name\n state\n}\n"
|
||||
"text": "query FrameworkGraphNodeQuery(\n $frameworkId: ID!\n) {\n node(id: $frameworkId) {\n __typename\n ... on Framework {\n id\n name\n ...FrameworkDetailPageFragment\n }\n id\n }\n}\n\nfragment FrameworkControlDialogFragment on Control {\n id\n name\n description\n sectionTitle\n}\n\nfragment FrameworkDetailPageFragment on Framework {\n id\n name\n description\n controls(first: 100) {\n edges {\n node {\n id\n sectionTitle\n name\n description\n ...FrameworkControlDialogFragment\n measures(first: 100) {\n edges {\n node {\n id\n ...LinkedMeasuresCardFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n documents(first: 100) {\n edges {\n node {\n id\n ...LinkedDocumentsCardFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n }\n }\n}\n\nfragment LinkedDocumentsCardFragment on Document {\n id\n title\n createdAt\n documentType\n versions(first: 1) {\n edges {\n node {\n id\n status\n }\n }\n }\n}\n\nfragment LinkedMeasuresCardFragment on Measure {\n id\n name\n state\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<705084da2fb7faefcfb2f4346e16565e>>
|
||||
* @generated SignedSource<<e7d82cc2843376766d45bdaac45f3b13>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -21,7 +21,7 @@ export type MeasureGraphNodeQuery$data = {
|
||||
readonly id?: string;
|
||||
readonly name?: string;
|
||||
readonly state?: MeasureState;
|
||||
readonly " $fragmentSpreads": FragmentRefs<"MeasureControlsTabFragment" | "MeasureEvidencesTabFragment" | "MeasureFormDialogMeasureFragment" | "MeasureRisksTabFragment">;
|
||||
readonly " $fragmentSpreads": FragmentRefs<"MeasureControlsTabFragment" | "MeasureEvidencesTabFragment" | "MeasureFormDialogMeasureFragment" | "MeasureRisksTabFragment" | "MeasureTasksTabFragment">;
|
||||
};
|
||||
};
|
||||
export type MeasureGraphNodeQuery = {
|
||||
@@ -174,6 +174,11 @@ return {
|
||||
"kind": "FragmentSpread",
|
||||
"name": "MeasureRisksTabFragment"
|
||||
},
|
||||
{
|
||||
"args": null,
|
||||
"kind": "FragmentSpread",
|
||||
"name": "MeasureTasksTabFragment"
|
||||
},
|
||||
{
|
||||
"args": null,
|
||||
"kind": "FragmentSpread",
|
||||
@@ -285,6 +290,101 @@ return {
|
||||
"kind": "LinkedHandle",
|
||||
"name": "risks"
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v8/*: any*/),
|
||||
"concreteType": "TaskConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "tasks",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "TaskEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "edges",
|
||||
"plural": true,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Task",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/),
|
||||
(v5/*: any*/),
|
||||
(v4/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "timeEstimate",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "deadline",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "People",
|
||||
"kind": "LinkedField",
|
||||
"name": "assignedTo",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "fullName",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Measure",
|
||||
"kind": "LinkedField",
|
||||
"name": "measure",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v2/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
(v7/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
(v9/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
(v12/*: any*/),
|
||||
(v13/*: any*/)
|
||||
],
|
||||
"storageKey": "tasks(first:100)"
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v8/*: any*/),
|
||||
"filters": null,
|
||||
"handle": "connection",
|
||||
"key": "Measure__tasks",
|
||||
"kind": "LinkedHandle",
|
||||
"name": "tasks"
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v8/*: any*/),
|
||||
@@ -480,16 +580,16 @@ return {
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "3c47df3d8325927791f007435186cf65",
|
||||
"cacheID": "b5f77889cfcec3b09ae7f6707c65200f",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "MeasureGraphNodeQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query MeasureGraphNodeQuery(\n $measureId: ID!\n) {\n node(id: $measureId) {\n __typename\n ... on Measure {\n id\n name\n description\n state\n category\n ...MeasureRisksTabFragment\n ...MeasureControlsTabFragment\n ...MeasureFormDialogMeasureFragment\n ...MeasureEvidencesTabFragment\n }\n id\n }\n}\n\nfragment LinkedControlsCardFragment on Control {\n id\n name\n sectionTitle\n framework {\n name\n id\n }\n}\n\nfragment LinkedRisksCardFragment on Risk {\n id\n name\n inherentRiskScore\n residualRiskScore\n}\n\nfragment MeasureControlsTabFragment on Measure {\n id\n controls(first: 100) {\n edges {\n node {\n id\n ...LinkedControlsCardFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n\nfragment MeasureEvidencesTabFragment on Measure {\n id\n evidences(first: 50) {\n edges {\n node {\n id\n ...MeasureEvidencesTabFragment_evidence\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n}\n\nfragment MeasureEvidencesTabFragment_evidence on Evidence {\n id\n filename\n size\n type\n createdAt\n fileUrl\n mimeType\n}\n\nfragment MeasureFormDialogMeasureFragment on Measure {\n id\n description\n name\n category\n state\n}\n\nfragment MeasureRisksTabFragment on Measure {\n id\n risks(first: 100) {\n edges {\n node {\n id\n ...LinkedRisksCardFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n"
|
||||
"text": "query MeasureGraphNodeQuery(\n $measureId: ID!\n) {\n node(id: $measureId) {\n __typename\n ... on Measure {\n id\n name\n description\n state\n category\n ...MeasureRisksTabFragment\n ...MeasureTasksTabFragment\n ...MeasureControlsTabFragment\n ...MeasureFormDialogMeasureFragment\n ...MeasureEvidencesTabFragment\n }\n id\n }\n}\n\nfragment LinkedControlsCardFragment on Control {\n id\n name\n sectionTitle\n framework {\n name\n id\n }\n}\n\nfragment LinkedRisksCardFragment on Risk {\n id\n name\n inherentRiskScore\n residualRiskScore\n}\n\nfragment MeasureControlsTabFragment on Measure {\n id\n controls(first: 100) {\n edges {\n node {\n id\n ...LinkedControlsCardFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n\nfragment MeasureEvidencesTabFragment on Measure {\n id\n evidences(first: 50) {\n edges {\n node {\n id\n ...MeasureEvidencesTabFragment_evidence\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n}\n\nfragment MeasureEvidencesTabFragment_evidence on Evidence {\n id\n filename\n size\n type\n createdAt\n fileUrl\n mimeType\n}\n\nfragment MeasureFormDialogMeasureFragment on Measure {\n id\n description\n name\n category\n state\n}\n\nfragment MeasureRisksTabFragment on Measure {\n id\n risks(first: 100) {\n edges {\n node {\n id\n ...LinkedRisksCardFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n\nfragment MeasureTasksTabFragment on Measure {\n tasks(first: 100) {\n edges {\n node {\n id\n name\n state\n description\n ...TaskFormDialogFragment\n assignedTo {\n id\n fullName\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n\nfragment TaskFormDialogFragment on Task {\n id\n description\n name\n state\n timeEstimate\n deadline\n assignedTo {\n id\n }\n measure {\n id\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "5c4bd0699414117e0ed408348554ca7d";
|
||||
(node as any).hash = "d5f21c1b7cd6cd1997018b1c0cfaee39";
|
||||
|
||||
export default node;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<987fdfe9c961d800699430963c5dba55>>
|
||||
* @generated SignedSource<<6f19d3ea0d16b3f4418e85261e3e4915>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -53,6 +53,20 @@ v3 = {
|
||||
"kind": "ScalarField",
|
||||
"name": "name",
|
||||
"storageKey": null
|
||||
},
|
||||
v4 = [
|
||||
{
|
||||
"kind": "Literal",
|
||||
"name": "first",
|
||||
"value": 100
|
||||
}
|
||||
],
|
||||
v5 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "createdAt",
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"fragment": {
|
||||
@@ -125,13 +139,56 @@ return {
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": [
|
||||
"args": (v4/*: any*/),
|
||||
"concreteType": "UserConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "users",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"kind": "Literal",
|
||||
"name": "first",
|
||||
"value": 100
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "UserEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "edges",
|
||||
"plural": true,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "User",
|
||||
"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": "email",
|
||||
"storageKey": null
|
||||
},
|
||||
(v5/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": "users(first:100)"
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v4/*: any*/),
|
||||
"concreteType": "ConnectorConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "connectors",
|
||||
@@ -162,13 +219,7 @@ return {
|
||||
"name": "type",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "createdAt",
|
||||
"storageKey": null
|
||||
}
|
||||
(v5/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
@@ -188,12 +239,12 @@ return {
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "66677d1e24f46cd551b4201d60c5e0a2",
|
||||
"cacheID": "3062be33ff59b816b10d098c083db370",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "OrganizationGraph_ViewQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query OrganizationGraph_ViewQuery(\n $organizationId: ID!\n) {\n node(id: $organizationId) {\n __typename\n ... on Organization {\n id\n name\n ...SettingsPageFragment\n }\n id\n }\n}\n\nfragment SettingsPageFragment on Organization {\n id\n name\n logoUrl\n connectors(first: 100) {\n edges {\n node {\n id\n name\n type\n createdAt\n }\n }\n }\n}\n"
|
||||
"text": "query OrganizationGraph_ViewQuery(\n $organizationId: ID!\n) {\n node(id: $organizationId) {\n __typename\n ... on Organization {\n id\n name\n ...SettingsPageFragment\n }\n id\n }\n}\n\nfragment SettingsPageFragment on Organization {\n id\n name\n logoUrl\n users(first: 100) {\n edges {\n node {\n id\n fullName\n email\n createdAt\n }\n }\n }\n connectors(first: 100) {\n edges {\n node {\n id\n name\n type\n createdAt\n }\n }\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
311
apps/console2/src/hooks/graph/__generated__/TaskGraphQuery.graphql.ts
generated
Normal file
311
apps/console2/src/hooks/graph/__generated__/TaskGraphQuery.graphql.ts
generated
Normal file
@@ -0,0 +1,311 @@
|
||||
/**
|
||||
* @generated SignedSource<<f73db66d7c3890608398fc933f073fd5>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
import { FragmentRefs } from "relay-runtime";
|
||||
export type TaskGraphQuery$variables = {
|
||||
organizationId: string;
|
||||
};
|
||||
export type TaskGraphQuery$data = {
|
||||
readonly organization: {
|
||||
readonly id?: string;
|
||||
readonly " $fragmentSpreads": FragmentRefs<"TasksPageFragment">;
|
||||
};
|
||||
};
|
||||
export type TaskGraphQuery = {
|
||||
response: TaskGraphQuery$data;
|
||||
variables: TaskGraphQuery$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": 20
|
||||
}
|
||||
],
|
||||
v5 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "name",
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "TaskGraphQuery",
|
||||
"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": "TasksPageFragment"
|
||||
}
|
||||
],
|
||||
"type": "Organization",
|
||||
"abstractKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Query",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "TaskGraphQuery",
|
||||
"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": "TaskConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "tasks",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "TaskEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "edges",
|
||||
"plural": true,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Task",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
(v5/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "state",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "description",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "timeEstimate",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "deadline",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "People",
|
||||
"kind": "LinkedField",
|
||||
"name": "assignedTo",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "fullName",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Measure",
|
||||
"kind": "LinkedField",
|
||||
"name": "measure",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
(v5/*: any*/)
|
||||
],
|
||||
"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": "tasks(first:20)"
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v4/*: any*/),
|
||||
"filters": [
|
||||
"orderBy"
|
||||
],
|
||||
"handle": "connection",
|
||||
"key": "TasksPageFragment_tasks",
|
||||
"kind": "LinkedHandle",
|
||||
"name": "tasks"
|
||||
}
|
||||
],
|
||||
"type": "Organization",
|
||||
"abstractKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "f80135be50e4f0a5a9a881930b686bcd",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "TaskGraphQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query TaskGraphQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n ... on Organization {\n id\n ...TasksPageFragment\n }\n id\n }\n}\n\nfragment TaskFormDialogFragment on Task {\n id\n description\n name\n state\n timeEstimate\n deadline\n assignedTo {\n id\n }\n measure {\n id\n }\n}\n\nfragment TasksPageFragment on Organization {\n tasks(first: 20) {\n edges {\n node {\n id\n name\n state\n description\n ...TaskFormDialogFragment\n measure {\n id\n name\n }\n assignedTo {\n id\n fullName\n }\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 = "86e37392ce0e0766dc2a083c64b2467e";
|
||||
|
||||
export default node;
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<47d66bf5d9b99eb598728999a7b2ce85>>
|
||||
* @generated SignedSource<<4b559d5e004d1657ab8d52cf5770ac79>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -11,7 +11,7 @@
|
||||
import { ReaderFragment } from 'relay-runtime';
|
||||
export type MeasureState = "IMPLEMENTED" | "IN_PROGRESS" | "NOT_APPLICABLE" | "NOT_STARTED";
|
||||
import { FragmentRefs } from "relay-runtime";
|
||||
export type LinkedMeasuresDialogFragment$data = {
|
||||
export type usePaginatedMeasuresFragment$data = {
|
||||
readonly id: string;
|
||||
readonly measures: {
|
||||
readonly edges: ReadonlyArray<{
|
||||
@@ -24,14 +24,14 @@ export type LinkedMeasuresDialogFragment$data = {
|
||||
};
|
||||
}>;
|
||||
};
|
||||
readonly " $fragmentType": "LinkedMeasuresDialogFragment";
|
||||
readonly " $fragmentType": "usePaginatedMeasuresFragment";
|
||||
};
|
||||
export type LinkedMeasuresDialogFragment$key = {
|
||||
readonly " $data"?: LinkedMeasuresDialogFragment$data;
|
||||
readonly " $fragmentSpreads": FragmentRefs<"LinkedMeasuresDialogFragment">;
|
||||
export type usePaginatedMeasuresFragment$key = {
|
||||
readonly " $data"?: usePaginatedMeasuresFragment$data;
|
||||
readonly " $fragmentSpreads": FragmentRefs<"usePaginatedMeasuresFragment">;
|
||||
};
|
||||
|
||||
import LinkedMeasuresDialogQuery_fragment_graphql from './LinkedMeasuresDialogQuery_fragment.graphql';
|
||||
import usePaginatedMeasuresQuery_fragment_graphql from './usePaginatedMeasuresQuery_fragment.graphql';
|
||||
|
||||
const node: ReaderFragment = (function(){
|
||||
var v0 = [
|
||||
@@ -97,14 +97,14 @@ return {
|
||||
"fragmentPathInResult": [
|
||||
"node"
|
||||
],
|
||||
"operation": LinkedMeasuresDialogQuery_fragment_graphql,
|
||||
"operation": usePaginatedMeasuresQuery_fragment_graphql,
|
||||
"identifierInfo": {
|
||||
"identifierField": "id",
|
||||
"identifierQueryVariableName": "id"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "LinkedMeasuresDialogFragment",
|
||||
"name": "usePaginatedMeasuresFragment",
|
||||
"selections": [
|
||||
{
|
||||
"alias": "measures",
|
||||
@@ -117,7 +117,7 @@ return {
|
||||
],
|
||||
"concreteType": "MeasureConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "__LinkedMeasuresDialogQuery_measures_connection",
|
||||
"name": "__usePaginatedMeasuresQuery_measures_connection",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
@@ -234,6 +234,6 @@ return {
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "69458f9aed8aee19f69cd58090b68947";
|
||||
(node as any).hash = "7d9401a3b7aad78951093dcac34df06e";
|
||||
|
||||
export default node;
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<d357c78d6043fd24cd4b9df8e6ba5057>>
|
||||
* @generated SignedSource<<30fa378f7e73bf788828131ecfccb812>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -10,18 +10,18 @@
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
import { FragmentRefs } from "relay-runtime";
|
||||
export type LinkedMeasuresDialogQuery$variables = {
|
||||
export type usePaginatedMeasuresQuery$variables = {
|
||||
organizationId: string;
|
||||
};
|
||||
export type LinkedMeasuresDialogQuery$data = {
|
||||
export type usePaginatedMeasuresQuery$data = {
|
||||
readonly organization: {
|
||||
readonly id: string;
|
||||
readonly " $fragmentSpreads": FragmentRefs<"LinkedMeasuresDialogFragment">;
|
||||
readonly " $fragmentSpreads": FragmentRefs<"usePaginatedMeasuresFragment">;
|
||||
};
|
||||
};
|
||||
export type LinkedMeasuresDialogQuery = {
|
||||
response: LinkedMeasuresDialogQuery$data;
|
||||
variables: LinkedMeasuresDialogQuery$variables;
|
||||
export type usePaginatedMeasuresQuery = {
|
||||
response: usePaginatedMeasuresQuery$data;
|
||||
variables: usePaginatedMeasuresQuery$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
@@ -65,7 +65,7 @@ return {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "LinkedMeasuresDialogQuery",
|
||||
"name": "usePaginatedMeasuresQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": "organization",
|
||||
@@ -82,7 +82,7 @@ return {
|
||||
{
|
||||
"args": null,
|
||||
"kind": "FragmentSpread",
|
||||
"name": "LinkedMeasuresDialogFragment"
|
||||
"name": "usePaginatedMeasuresFragment"
|
||||
}
|
||||
],
|
||||
"type": "Organization",
|
||||
@@ -99,7 +99,7 @@ return {
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "LinkedMeasuresDialogQuery",
|
||||
"name": "usePaginatedMeasuresQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": "organization",
|
||||
@@ -230,7 +230,7 @@ return {
|
||||
"orderBy"
|
||||
],
|
||||
"handle": "connection",
|
||||
"key": "LinkedMeasuresDialogQuery_measures",
|
||||
"key": "usePaginatedMeasuresQuery_measures",
|
||||
"kind": "LinkedHandle",
|
||||
"name": "measures"
|
||||
}
|
||||
@@ -244,16 +244,16 @@ return {
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "ecb3ffb566c178a40f6ea72b1947b3ed",
|
||||
"cacheID": "5e37746b5ff660a2b5c817eb64ec0f6e",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "LinkedMeasuresDialogQuery",
|
||||
"name": "usePaginatedMeasuresQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query LinkedMeasuresDialogQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n id\n ... on Organization {\n ...LinkedMeasuresDialogFragment\n }\n }\n}\n\nfragment LinkedMeasuresDialogFragment on Organization {\n measures(first: 20) {\n edges {\n node {\n id\n name\n state\n description\n category\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n"
|
||||
"text": "query usePaginatedMeasuresQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n id\n ... on Organization {\n ...usePaginatedMeasuresFragment\n }\n }\n}\n\nfragment usePaginatedMeasuresFragment on Organization {\n measures(first: 20) {\n edges {\n node {\n id\n name\n state\n description\n category\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 = "8592f3602ea10f6c00f5eb32c3f6b638";
|
||||
(node as any).hash = "49bbe1012e06a7c38d335da0defc0270";
|
||||
|
||||
export default node;
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<412b6fd2ef44aa7bc3c9e66ec2284c37>>
|
||||
* @generated SignedSource<<fa04c379d9d79cc9d49a08c4ed70b994>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -16,7 +16,7 @@ export type MeasureOrder = {
|
||||
direction: OrderDirection;
|
||||
field: MeasureOrderField;
|
||||
};
|
||||
export type LinkedMeasuresDialogQuery_fragment$variables = {
|
||||
export type usePaginatedMeasuresQuery_fragment$variables = {
|
||||
after?: any | null | undefined;
|
||||
before?: any | null | undefined;
|
||||
first?: number | null | undefined;
|
||||
@@ -24,14 +24,14 @@ export type LinkedMeasuresDialogQuery_fragment$variables = {
|
||||
last?: number | null | undefined;
|
||||
order?: MeasureOrder | null | undefined;
|
||||
};
|
||||
export type LinkedMeasuresDialogQuery_fragment$data = {
|
||||
export type usePaginatedMeasuresQuery_fragment$data = {
|
||||
readonly node: {
|
||||
readonly " $fragmentSpreads": FragmentRefs<"LinkedMeasuresDialogFragment">;
|
||||
readonly " $fragmentSpreads": FragmentRefs<"usePaginatedMeasuresFragment">;
|
||||
};
|
||||
};
|
||||
export type LinkedMeasuresDialogQuery_fragment = {
|
||||
response: LinkedMeasuresDialogQuery_fragment$data;
|
||||
variables: LinkedMeasuresDialogQuery_fragment$variables;
|
||||
export type usePaginatedMeasuresQuery_fragment = {
|
||||
response: usePaginatedMeasuresQuery_fragment$data;
|
||||
variables: usePaginatedMeasuresQuery_fragment$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
@@ -129,7 +129,7 @@ return {
|
||||
],
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "LinkedMeasuresDialogQuery_fragment",
|
||||
"name": "usePaginatedMeasuresQuery_fragment",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
@@ -152,7 +152,7 @@ return {
|
||||
}
|
||||
],
|
||||
"kind": "FragmentSpread",
|
||||
"name": "LinkedMeasuresDialogFragment"
|
||||
"name": "usePaginatedMeasuresFragment"
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
@@ -172,7 +172,7 @@ return {
|
||||
(v3/*: any*/)
|
||||
],
|
||||
"kind": "Operation",
|
||||
"name": "LinkedMeasuresDialogQuery_fragment",
|
||||
"name": "usePaginatedMeasuresQuery_fragment",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
@@ -303,7 +303,7 @@ return {
|
||||
"orderBy"
|
||||
],
|
||||
"handle": "connection",
|
||||
"key": "LinkedMeasuresDialogQuery_measures",
|
||||
"key": "usePaginatedMeasuresQuery_measures",
|
||||
"kind": "LinkedHandle",
|
||||
"name": "measures"
|
||||
}
|
||||
@@ -317,16 +317,16 @@ return {
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "9de5e8b136a2952dbb169f6b27aade30",
|
||||
"cacheID": "c37a9764fcf0b587ae467395a5af84e4",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "LinkedMeasuresDialogQuery_fragment",
|
||||
"name": "usePaginatedMeasuresQuery_fragment",
|
||||
"operationKind": "query",
|
||||
"text": "query LinkedMeasuresDialogQuery_fragment(\n $after: CursorKey = null\n $before: CursorKey = null\n $first: Int = 20\n $last: Int = null\n $order: MeasureOrder = null\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...LinkedMeasuresDialogFragment_16fISc\n id\n }\n}\n\nfragment LinkedMeasuresDialogFragment_16fISc on Organization {\n measures(first: $first, after: $after, last: $last, before: $before, orderBy: $order) {\n edges {\n node {\n id\n name\n state\n description\n category\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n"
|
||||
"text": "query usePaginatedMeasuresQuery_fragment(\n $after: CursorKey = null\n $before: CursorKey = null\n $first: Int = 20\n $last: Int = null\n $order: MeasureOrder = null\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...usePaginatedMeasuresFragment_16fISc\n id\n }\n}\n\nfragment usePaginatedMeasuresFragment_16fISc on Organization {\n measures(first: $first, after: $after, last: $last, before: $before, orderBy: $order) {\n edges {\n node {\n id\n name\n state\n description\n category\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 = "69458f9aed8aee19f69cd58090b68947";
|
||||
(node as any).hash = "7d9401a3b7aad78951093dcac34df06e";
|
||||
|
||||
export default node;
|
||||
57
apps/console2/src/hooks/graph/usePaginatedMeasures.ts
Normal file
57
apps/console2/src/hooks/graph/usePaginatedMeasures.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { graphql, useLazyLoadQuery, usePaginationFragment } from "react-relay";
|
||||
import type { usePaginatedMeasuresQuery } from "./__generated__/usePaginatedMeasuresQuery.graphql";
|
||||
import type { usePaginatedMeasuresFragment$key } from "./__generated__/usePaginatedMeasuresFragment.graphql";
|
||||
|
||||
const measuresQuery = graphql`
|
||||
query usePaginatedMeasuresQuery($organizationId: ID!) {
|
||||
organization: node(id: $organizationId) {
|
||||
id
|
||||
... on Organization {
|
||||
...usePaginatedMeasuresFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const measuresFragment = graphql`
|
||||
fragment usePaginatedMeasuresFragment on Organization
|
||||
@refetchable(queryName: "usePaginatedMeasuresQuery_fragment")
|
||||
@argumentDefinitions(
|
||||
first: { type: "Int", defaultValue: 20 }
|
||||
order: { type: "MeasureOrder", defaultValue: null }
|
||||
after: { type: "CursorKey", defaultValue: null }
|
||||
before: { type: "CursorKey", defaultValue: null }
|
||||
last: { type: "Int", defaultValue: null }
|
||||
) {
|
||||
measures(
|
||||
first: $first
|
||||
after: $after
|
||||
last: $last
|
||||
before: $before
|
||||
orderBy: $order
|
||||
) @connection(key: "usePaginatedMeasuresQuery_measures") {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
name
|
||||
state
|
||||
description
|
||||
category
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* Hook to retrieve measured paginated (used for link dialog and measure selectors)
|
||||
*/
|
||||
export function usePaginatedMeasures(organizationId: string) {
|
||||
const query = useLazyLoadQuery<usePaginatedMeasuresQuery>(measuresQuery, {
|
||||
organizationId,
|
||||
});
|
||||
return usePaginationFragment(
|
||||
measuresFragment,
|
||||
query.organization as usePaginatedMeasuresFragment$key
|
||||
);
|
||||
}
|
||||
@@ -1,13 +1,17 @@
|
||||
import {
|
||||
ActionDropdown,
|
||||
Avatar,
|
||||
Badge,
|
||||
Button,
|
||||
Card,
|
||||
DropdownItem,
|
||||
Field,
|
||||
FileButton,
|
||||
IconTrashCan,
|
||||
Label,
|
||||
PageHeader,
|
||||
Spinner,
|
||||
useConfirm,
|
||||
useToast,
|
||||
} from "@probo/ui";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
@@ -21,9 +25,13 @@ import type {
|
||||
SettingsPageFragment$data,
|
||||
SettingsPageFragment$key,
|
||||
} from "./__generated__/SettingsPageFragment.graphql";
|
||||
import type { ChangeEventHandler } from "react";
|
||||
import { useState, type ChangeEventHandler } from "react";
|
||||
import { sprintf } from "@probo/helpers";
|
||||
import type { NodeOf } from "/types";
|
||||
import clsx from "clsx";
|
||||
import { useMutationWithToasts } from "/hooks/useMutationWithToasts";
|
||||
import { useOrganizationId } from "/hooks/useOrganizationId";
|
||||
import { InviteUserDialog } from "/components/organizations/InviteUserDialog";
|
||||
|
||||
type Props = {
|
||||
queryRef: PreloadedQuery<OrganizationGraph_ViewQuery>;
|
||||
@@ -34,6 +42,16 @@ const organizationFragment = graphql`
|
||||
id
|
||||
name
|
||||
logoUrl
|
||||
users(first: 100) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
fullName
|
||||
email
|
||||
createdAt
|
||||
}
|
||||
}
|
||||
}
|
||||
connectors(first: 100) {
|
||||
edges {
|
||||
node {
|
||||
@@ -73,6 +91,7 @@ export default function SettingsPage({ queryRef }: Props) {
|
||||
const [updateOrganization, isUpdating] = useMutation(
|
||||
updateOrganizationMutation
|
||||
);
|
||||
const users = organization.users.edges.map((edge) => edge.node);
|
||||
|
||||
const updateOrganizationName = useDebounceCallback((name: string) => {
|
||||
if (!name) {
|
||||
@@ -156,6 +175,21 @@ export default function SettingsPage({ queryRef }: Props) {
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Integrations */}
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<h2 className="text-base font-medium">{__("Workspace members")}</h2>
|
||||
<InviteUserDialog>
|
||||
<Button variant="secondary">{__("Invite member")}</Button>
|
||||
</InviteUserDialog>
|
||||
</div>
|
||||
<Card className="divide-y divide-border-solid">
|
||||
{users.map((user) => (
|
||||
<UserRow key={user.id} user={user} />
|
||||
))}
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Integrations */}
|
||||
<div className="space-y-4">
|
||||
<h2 className="text-base font-medium">{__("Integrations")}</h2>
|
||||
@@ -243,3 +277,86 @@ function Connectors(props: {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const removeUserMutation = graphql`
|
||||
mutation SettingsPage_RemoveUserMutation($input: RemoveUserInput!) {
|
||||
removeUser(input: $input) {
|
||||
success
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
function UserRow(props: { user: NodeOf<SettingsPageFragment$data["users"]> }) {
|
||||
const { __ } = useTranslate();
|
||||
const organizationId = useOrganizationId();
|
||||
const [removeUser, isRemoving] = useMutationWithToasts(removeUserMutation, {
|
||||
successMessage: sprintf(
|
||||
__("User %s removed successfully"),
|
||||
props.user.fullName
|
||||
),
|
||||
errorMessage: sprintf(__("Failed to remove user %s"), props.user.fullName),
|
||||
});
|
||||
const confirm = useConfirm();
|
||||
const [isRemoved, setIsRemoved] = useState(false);
|
||||
|
||||
if (isRemoved) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const onRemove = async () => {
|
||||
confirm(
|
||||
() => {
|
||||
return removeUser({
|
||||
variables: {
|
||||
input: {
|
||||
userId: props.user.id,
|
||||
organizationId: organizationId,
|
||||
},
|
||||
},
|
||||
onSuccess: () => {
|
||||
setIsRemoved(true);
|
||||
},
|
||||
});
|
||||
},
|
||||
{
|
||||
message: sprintf(
|
||||
__("Are you sure you want to remove %s?"),
|
||||
props.user.fullName
|
||||
),
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
"flex justify-between items-center py-4 px-4",
|
||||
isRemoving && "opacity-60 pointer-events-none"
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center gap-4">
|
||||
<Avatar name={props.user.fullName} size="l" />
|
||||
<div>
|
||||
<h3 className="text-base font-semibold">{props.user.fullName}</h3>
|
||||
<p className="text-sm text-txt-tertiary">{props.user.email}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2 items-center">
|
||||
<Badge>{__("Owner")}</Badge>
|
||||
{isRemoving ? (
|
||||
<Spinner size={16} />
|
||||
) : (
|
||||
<ActionDropdown>
|
||||
<DropdownItem
|
||||
variant="danger"
|
||||
icon={IconTrashCan}
|
||||
onClick={onRemove}
|
||||
>
|
||||
{__("Remove")}
|
||||
</DropdownItem>
|
||||
</ActionDropdown>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<02bed9e8295eda48e8fa5c0ced3ca1f6>>
|
||||
* @generated SignedSource<<e5b6c1db04175ebddb21379dfd331fc6>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -24,6 +24,16 @@ export type SettingsPageFragment$data = {
|
||||
readonly id: string;
|
||||
readonly logoUrl: string | null | undefined;
|
||||
readonly name: string;
|
||||
readonly users: {
|
||||
readonly edges: ReadonlyArray<{
|
||||
readonly node: {
|
||||
readonly createdAt: any;
|
||||
readonly email: string;
|
||||
readonly fullName: string;
|
||||
readonly id: string;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
readonly " $fragmentType": "SettingsPageFragment";
|
||||
};
|
||||
export type SettingsPageFragment$key = {
|
||||
@@ -45,6 +55,20 @@ v1 = {
|
||||
"kind": "ScalarField",
|
||||
"name": "name",
|
||||
"storageKey": null
|
||||
},
|
||||
v2 = [
|
||||
{
|
||||
"kind": "Literal",
|
||||
"name": "first",
|
||||
"value": 100
|
||||
}
|
||||
],
|
||||
v3 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "createdAt",
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"argumentDefinitions": [],
|
||||
@@ -63,13 +87,56 @@ return {
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": [
|
||||
"args": (v2/*: any*/),
|
||||
"concreteType": "UserConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "users",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"kind": "Literal",
|
||||
"name": "first",
|
||||
"value": 100
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "UserEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "edges",
|
||||
"plural": true,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "User",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v0/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "fullName",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "email",
|
||||
"storageKey": null
|
||||
},
|
||||
(v3/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": "users(first:100)"
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v2/*: any*/),
|
||||
"concreteType": "ConnectorConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "connectors",
|
||||
@@ -100,13 +167,7 @@ return {
|
||||
"name": "type",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "createdAt",
|
||||
"storageKey": null
|
||||
}
|
||||
(v3/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
@@ -122,6 +183,6 @@ return {
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "ecff581e2f458947e8e8b830fa3d5005";
|
||||
(node as any).hash = "b7ee53afcaaa0406dceeda0427afdfef";
|
||||
|
||||
export default node;
|
||||
|
||||
93
apps/console2/src/pages/organizations/__generated__/SettingsPage_RemoveUserMutation.graphql.ts
generated
Normal file
93
apps/console2/src/pages/organizations/__generated__/SettingsPage_RemoveUserMutation.graphql.ts
generated
Normal file
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* @generated SignedSource<<e5fd0bf99e25c4f410c6c53f4025ed15>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type RemoveUserInput = {
|
||||
organizationId: string;
|
||||
userId: string;
|
||||
};
|
||||
export type SettingsPage_RemoveUserMutation$variables = {
|
||||
input: RemoveUserInput;
|
||||
};
|
||||
export type SettingsPage_RemoveUserMutation$data = {
|
||||
readonly removeUser: {
|
||||
readonly success: boolean;
|
||||
};
|
||||
};
|
||||
export type SettingsPage_RemoveUserMutation = {
|
||||
response: SettingsPage_RemoveUserMutation$data;
|
||||
variables: SettingsPage_RemoveUserMutation$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "input"
|
||||
}
|
||||
],
|
||||
v1 = [
|
||||
{
|
||||
"alias": null,
|
||||
"args": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "input",
|
||||
"variableName": "input"
|
||||
}
|
||||
],
|
||||
"concreteType": "RemoveUserPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "removeUser",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "success",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
];
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "SettingsPage_RemoveUserMutation",
|
||||
"selections": (v1/*: any*/),
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "SettingsPage_RemoveUserMutation",
|
||||
"selections": (v1/*: any*/)
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "a397335d917a93baf77ceba83c735326",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "SettingsPage_RemoveUserMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation SettingsPage_RemoveUserMutation(\n $input: RemoveUserInput!\n) {\n removeUser(input: $input) {\n success\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "ba5bf0e182d15f4805080f63d1604d91";
|
||||
|
||||
export default node;
|
||||
@@ -33,6 +33,7 @@ const createDocumentMutation = graphql`
|
||||
createDocument(input: $input) {
|
||||
documentEdge @prependEdge(connections: $connections) {
|
||||
node {
|
||||
id
|
||||
...DocumentsPageRowFragment
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<828dc0f47cb0d218fc437848beac7878>>
|
||||
* @generated SignedSource<<99814a217713cd574beb65c91802dc74>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -26,6 +26,7 @@ export type CreateDocumentDialogMutation$data = {
|
||||
readonly createDocument: {
|
||||
readonly documentEdge: {
|
||||
readonly node: {
|
||||
readonly id: string;
|
||||
readonly " $fragmentSpreads": FragmentRefs<"DocumentsPageRowFragment">;
|
||||
};
|
||||
};
|
||||
@@ -95,6 +96,7 @@ return {
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/),
|
||||
{
|
||||
"args": null,
|
||||
"kind": "FragmentSpread",
|
||||
@@ -309,16 +311,16 @@ return {
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "bedd217ae32d45e8ac183a38e5624618",
|
||||
"cacheID": "d535d393c2ab2e1a9798d193bcc8c17f",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "CreateDocumentDialogMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation CreateDocumentDialogMutation(\n $input: CreateDocumentInput!\n) {\n createDocument(input: $input) {\n documentEdge {\n node {\n ...DocumentsPageRowFragment\n id\n }\n }\n }\n}\n\nfragment DocumentsPageRowFragment on Document {\n id\n title\n description\n updatedAt\n owner {\n id\n fullName\n }\n versions(first: 1) {\n edges {\n node {\n id\n status\n signatures(first: 100) {\n edges {\n node {\n id\n state\n }\n }\n }\n }\n }\n }\n}\n"
|
||||
"text": "mutation CreateDocumentDialogMutation(\n $input: CreateDocumentInput!\n) {\n createDocument(input: $input) {\n documentEdge {\n node {\n id\n ...DocumentsPageRowFragment\n }\n }\n }\n}\n\nfragment DocumentsPageRowFragment on Document {\n id\n title\n description\n updatedAt\n owner {\n id\n fullName\n }\n versions(first: 1) {\n edges {\n node {\n id\n status\n signatures(first: 100) {\n edges {\n node {\n id\n state\n }\n }\n }\n }\n }\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "f3792f9bd5524dd1e7656908c2774069";
|
||||
(node as any).hash = "19d6bd18594154b918c529d15f7ee8d7";
|
||||
|
||||
export default node;
|
||||
|
||||
@@ -12,8 +12,10 @@ import {
|
||||
ControlItem,
|
||||
DropdownItem,
|
||||
IconPencil,
|
||||
IconPlusLarge,
|
||||
IconTrashCan,
|
||||
PageHeader,
|
||||
useConfirm,
|
||||
} from "@probo/ui";
|
||||
import { FrameworkLogo } from "/components/FrameworkLogo";
|
||||
import {
|
||||
@@ -26,9 +28,16 @@ import { LinkedMeasuresCard } from "/components/measures/LinkedMeasuresCard";
|
||||
import { useNavigate, useParams } from "react-router";
|
||||
import { useOrganizationId } from "/hooks/useOrganizationId";
|
||||
import type { FrameworkGraphNodeQuery } from "/hooks/graph/__generated__/FrameworkGraphNodeQuery.graphql";
|
||||
import type { FrameworkDetailPageFragment$key } from "./__generated__/FrameworkDetailPageFragment.graphql";
|
||||
import type {
|
||||
FrameworkDetailPageFragment$data,
|
||||
FrameworkDetailPageFragment$key,
|
||||
} from "./__generated__/FrameworkDetailPageFragment.graphql";
|
||||
import { LinkedDocumentsCard } from "/components/documents/LinkedDocumentsCard";
|
||||
import { FrameworkFormDialog } from "./dialogs/FrameworkFormDialog";
|
||||
import { FrameworkControlDialog } from "./dialogs/FrameworkControlDialog";
|
||||
import type { FrameworkControlDialogFragment$key } from "./dialogs/__generated__/FrameworkControlDialogFragment.graphql";
|
||||
import type { NodeOf } from "/types";
|
||||
import { promisifyMutation } from "@probo/helpers";
|
||||
|
||||
const frameworkDetailFragment = graphql`
|
||||
fragment FrameworkDetailPageFragment on Framework {
|
||||
@@ -36,12 +45,14 @@ const frameworkDetailFragment = graphql`
|
||||
name
|
||||
description
|
||||
controls(first: 100) {
|
||||
__id
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
sectionTitle
|
||||
name
|
||||
description
|
||||
...FrameworkControlDialogFragment
|
||||
measures(first: 100)
|
||||
@connection(key: "FrameworkDetailPage_measures") {
|
||||
__id
|
||||
@@ -140,26 +151,13 @@ export default function FrameworkDetailPage(props: Props) {
|
||||
const selectedControl = controlId
|
||||
? controls.find((control) => control.id === controlId)
|
||||
: controls[0];
|
||||
usePageTitle(`${framework.name} | ${selectedControl?.sectionTitle}`);
|
||||
|
||||
// Mutations
|
||||
const [detachMeasure, isDetachingMeasure] = useMutation(
|
||||
detachMeasureMutation
|
||||
);
|
||||
const [attachMeasure, isAttachingMeasure] = useMutation(
|
||||
attachMeasureMutation
|
||||
);
|
||||
const [detachDocument, isDetachingDocument] = useMutation(
|
||||
detachDocumentMutation
|
||||
);
|
||||
const [attachDocument, isAttachingDocument] = useMutation(
|
||||
attachDocumentMutation
|
||||
);
|
||||
const connectionId = framework.controls.__id;
|
||||
const deleteFramework = useDeleteFrameworkMutation(
|
||||
framework,
|
||||
ConnectionHandler.getConnectionID(organizationId, connectionListKey)!
|
||||
);
|
||||
|
||||
usePageTitle(`${framework.name} | ${selectedControl?.sectionTitle}`);
|
||||
const onDelete = () => {
|
||||
deleteFramework({
|
||||
onSuccess: () => {
|
||||
@@ -209,36 +207,22 @@ export default function FrameworkDetailPage(props: Props) {
|
||||
active={selectedControl?.id === control.id}
|
||||
/>
|
||||
))}
|
||||
<FrameworkControlDialog
|
||||
frameworkId={framework.id}
|
||||
connectionId={connectionId}
|
||||
>
|
||||
<button className="flex gap-[6px] flex-col w-full p-4 space-y-[6px] rounded-xl cursor-pointer text-start text-sm text-txt-tertiary hover:bg-tertiary-hover">
|
||||
<IconPlusLarge size={20} className="text-txt-primary" />
|
||||
{__("Add new control")}
|
||||
</button>
|
||||
</FrameworkControlDialog>
|
||||
</div>
|
||||
{selectedControl ? (
|
||||
<div className="space-y-6">
|
||||
<div className="text-xl font-medium px-[6px] py-[2px] border border-border-low rounded-lg w-max bg-active mb-3">
|
||||
{selectedControl.sectionTitle}
|
||||
</div>
|
||||
<div className="text-base">{selectedControl.name}</div>
|
||||
<LinkedMeasuresCard
|
||||
variant="card"
|
||||
measures={
|
||||
selectedControl?.measures.edges.map((edge) => edge.node) ?? []
|
||||
}
|
||||
params={{ controlId: selectedControl.id }}
|
||||
connectionId={selectedControl.measures.__id!}
|
||||
onAttach={attachMeasure}
|
||||
onDetach={detachMeasure}
|
||||
disabled={isAttachingMeasure || isDetachingMeasure}
|
||||
/>
|
||||
<LinkedDocumentsCard
|
||||
variant="card"
|
||||
documents={
|
||||
selectedControl?.documents.edges.map((edge) => edge.node) ?? []
|
||||
}
|
||||
params={{ controlId: selectedControl.id }}
|
||||
connectionId={selectedControl.documents.__id!}
|
||||
onAttach={attachDocument}
|
||||
onDetach={detachDocument}
|
||||
disabled={isAttachingDocument || isDetachingDocument}
|
||||
/>
|
||||
</div>
|
||||
<ControlContent
|
||||
control={selectedControl}
|
||||
frameworkId={framework.id}
|
||||
connectionId={connectionId}
|
||||
/>
|
||||
) : (
|
||||
<div className="h-full flex items-center justify-center">
|
||||
<div className="text-sm text-txt-secondary">
|
||||
@@ -250,3 +234,120 @@ export default function FrameworkDetailPage(props: Props) {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const deleteControlMutation = graphql`
|
||||
mutation FrameworkDetailPageDeleteControlMutation(
|
||||
$input: DeleteControlInput!
|
||||
$connections: [ID!]!
|
||||
) {
|
||||
deleteControl(input: $input) {
|
||||
deletedControlId @deleteEdge(connections: $connections)
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* Right side pannel (showing the content of the selected control)
|
||||
*/
|
||||
function ControlContent({
|
||||
control,
|
||||
frameworkId,
|
||||
connectionId,
|
||||
}: {
|
||||
control: NodeOf<FrameworkDetailPageFragment$data["controls"]> &
|
||||
FrameworkControlDialogFragment$key;
|
||||
frameworkId: string;
|
||||
connectionId: string;
|
||||
}) {
|
||||
const { __ } = useTranslate();
|
||||
const organizationId = useOrganizationId();
|
||||
const confirm = useConfirm();
|
||||
const navigate = useNavigate();
|
||||
// Mutations
|
||||
const [detachMeasure, isDetachingMeasure] = useMutation(
|
||||
detachMeasureMutation
|
||||
);
|
||||
const [attachMeasure, isAttachingMeasure] = useMutation(
|
||||
attachMeasureMutation
|
||||
);
|
||||
const [detachDocument, isDetachingDocument] = useMutation(
|
||||
detachDocumentMutation
|
||||
);
|
||||
const [attachDocument, isAttachingDocument] = useMutation(
|
||||
attachDocumentMutation
|
||||
);
|
||||
const [deleteControl] = useMutation(deleteControlMutation);
|
||||
|
||||
const onDelete = () => {
|
||||
confirm(
|
||||
() => {
|
||||
return promisifyMutation(deleteControl)({
|
||||
variables: {
|
||||
input: {
|
||||
controlId: control.id,
|
||||
},
|
||||
connections: [connectionId],
|
||||
},
|
||||
onCompleted: () => {
|
||||
navigate(
|
||||
`/organizations/${organizationId}/frameworks/${frameworkId}`
|
||||
);
|
||||
},
|
||||
});
|
||||
},
|
||||
{
|
||||
message: __("Are you sure you want to delete this control?"),
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex justify-between">
|
||||
<div className="text-xl font-medium px-[6px] py-[2px] border border-border-low rounded-lg w-max bg-active mb-3">
|
||||
{control.sectionTitle}
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<FrameworkControlDialog
|
||||
frameworkId={frameworkId}
|
||||
connectionId={connectionId}
|
||||
control={control}
|
||||
>
|
||||
<Button icon={IconPencil} variant="secondary">
|
||||
{__("Edit control")}
|
||||
</Button>
|
||||
</FrameworkControlDialog>
|
||||
<ActionDropdown variant="secondary">
|
||||
<DropdownItem
|
||||
icon={IconTrashCan}
|
||||
variant="danger"
|
||||
onClick={onDelete}
|
||||
>
|
||||
{__("Delete")}
|
||||
</DropdownItem>
|
||||
</ActionDropdown>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="text-base">{control.name}</div>
|
||||
<LinkedMeasuresCard
|
||||
variant="card"
|
||||
measures={control?.measures.edges.map((edge) => edge.node) ?? []}
|
||||
params={{ controlId: control.id }}
|
||||
connectionId={control.measures.__id!}
|
||||
onAttach={attachMeasure}
|
||||
onDetach={detachMeasure}
|
||||
disabled={isAttachingMeasure || isDetachingMeasure}
|
||||
/>
|
||||
<LinkedDocumentsCard
|
||||
variant="card"
|
||||
documents={control?.documents.edges.map((edge) => edge.node) ?? []}
|
||||
params={{ controlId: control.id }}
|
||||
connectionId={control.documents.__id!}
|
||||
onAttach={attachDocument}
|
||||
onDetach={detachDocument}
|
||||
disabled={isAttachingDocument || isDetachingDocument}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* @generated SignedSource<<ed7ba35de62460b7625419b78b67997b>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type DeleteControlInput = {
|
||||
controlId: string;
|
||||
};
|
||||
export type FrameworkDetailPageDeleteControlMutation$variables = {
|
||||
connections: ReadonlyArray<string>;
|
||||
input: DeleteControlInput;
|
||||
};
|
||||
export type FrameworkDetailPageDeleteControlMutation$data = {
|
||||
readonly deleteControl: {
|
||||
readonly deletedControlId: string;
|
||||
};
|
||||
};
|
||||
export type FrameworkDetailPageDeleteControlMutation = {
|
||||
response: FrameworkDetailPageDeleteControlMutation$data;
|
||||
variables: FrameworkDetailPageDeleteControlMutation$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": "deletedControlId",
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": [
|
||||
(v0/*: any*/),
|
||||
(v1/*: any*/)
|
||||
],
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "FrameworkDetailPageDeleteControlMutation",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v2/*: any*/),
|
||||
"concreteType": "DeleteControlPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "deleteControl",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": [
|
||||
(v1/*: any*/),
|
||||
(v0/*: any*/)
|
||||
],
|
||||
"kind": "Operation",
|
||||
"name": "FrameworkDetailPageDeleteControlMutation",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v2/*: any*/),
|
||||
"concreteType": "DeleteControlPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "deleteControl",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"filters": null,
|
||||
"handle": "deleteEdge",
|
||||
"key": "",
|
||||
"kind": "ScalarHandle",
|
||||
"name": "deletedControlId",
|
||||
"handleArgs": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "connections",
|
||||
"variableName": "connections"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "d86c7a2bd535a98d1cb7308a96c13401",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "FrameworkDetailPageDeleteControlMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation FrameworkDetailPageDeleteControlMutation(\n $input: DeleteControlInput!\n) {\n deleteControl(input: $input) {\n deletedControlId\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "a067e712c3c51b73abac50e6c3779141";
|
||||
|
||||
export default node;
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<3e2b13f2b9a543d8393fdb7a1d7c680d>>
|
||||
* @generated SignedSource<<775e1361b15523efe196b9edbd406f5a>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -12,6 +12,7 @@ import { ReaderFragment } from 'relay-runtime';
|
||||
import { FragmentRefs } from "relay-runtime";
|
||||
export type FrameworkDetailPageFragment$data = {
|
||||
readonly controls: {
|
||||
readonly __id: string;
|
||||
readonly edges: ReadonlyArray<{
|
||||
readonly node: {
|
||||
readonly description: string;
|
||||
@@ -36,6 +37,7 @@ export type FrameworkDetailPageFragment$data = {
|
||||
};
|
||||
readonly name: string;
|
||||
readonly sectionTitle: string;
|
||||
readonly " $fragmentSpreads": FragmentRefs<"FrameworkControlDialogFragment">;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
@@ -182,6 +184,11 @@ return {
|
||||
},
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/),
|
||||
{
|
||||
"args": null,
|
||||
"kind": "FragmentSpread",
|
||||
"name": "FrameworkControlDialogFragment"
|
||||
},
|
||||
{
|
||||
"alias": "measures",
|
||||
"args": null,
|
||||
@@ -273,7 +280,8 @@ return {
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
},
|
||||
(v7/*: any*/)
|
||||
],
|
||||
"storageKey": "controls(first:100)"
|
||||
}
|
||||
@@ -283,6 +291,6 @@ return {
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "f0fe30240833b2da754f040f0c35aaaa";
|
||||
(node as any).hash = "b70c5b2090c5db158c44f0b9fcc0f9bc";
|
||||
|
||||
export default node;
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
import {
|
||||
Breadcrumb,
|
||||
Button,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
Input,
|
||||
Textarea,
|
||||
useDialogRef,
|
||||
} from "@probo/ui";
|
||||
import type { ReactNode } from "react";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { graphql } from "relay-runtime";
|
||||
import { useFragment } from "react-relay";
|
||||
import type { FrameworkControlDialogFragment$key } from "./__generated__/FrameworkControlDialogFragment.graphql";
|
||||
import { useFormWithSchema } from "/hooks/useFormWithSchema";
|
||||
import { z } from "zod";
|
||||
import { useMutationWithToasts } from "/hooks/useMutationWithToasts";
|
||||
|
||||
type Props = {
|
||||
children: ReactNode;
|
||||
control?: FrameworkControlDialogFragment$key;
|
||||
frameworkId: string;
|
||||
connectionId?: string;
|
||||
};
|
||||
|
||||
const controlFragment = graphql`
|
||||
fragment FrameworkControlDialogFragment on Control {
|
||||
id
|
||||
name
|
||||
description
|
||||
sectionTitle
|
||||
}
|
||||
`;
|
||||
|
||||
const createMutation = graphql`
|
||||
mutation FrameworkControlDialogCreateMutation(
|
||||
$input: CreateControlInput!
|
||||
$connections: [ID!]!
|
||||
) {
|
||||
createControl(input: $input) {
|
||||
controlEdge @prependEdge(connections: $connections) {
|
||||
node {
|
||||
...FrameworkControlDialogFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const updateMutation = graphql`
|
||||
mutation FrameworkControlDialogUpdateMutation($input: UpdateControlInput!) {
|
||||
updateControl(input: $input) {
|
||||
control {
|
||||
...FrameworkControlDialogFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const schema = z.object({
|
||||
name: z.string(),
|
||||
description: z.string(),
|
||||
sectionTitle: z.string(),
|
||||
});
|
||||
|
||||
export function FrameworkControlDialog(props: Props) {
|
||||
const { __ } = useTranslate();
|
||||
const frameworkControl = useFragment(controlFragment, props.control);
|
||||
const dialogRef = useDialogRef();
|
||||
const [mutate, isMutating] = props.control
|
||||
? useMutationWithToasts(updateMutation, {
|
||||
successMessage: __("Control updated successfully."),
|
||||
errorMessage: __("Failed to update control. Please try again."),
|
||||
})
|
||||
: useMutationWithToasts(createMutation, {
|
||||
successMessage: __("Control created successfully."),
|
||||
errorMessage: __("Failed to create control. Please try again."),
|
||||
});
|
||||
const { control, handleSubmit, register } = useFormWithSchema(schema, {
|
||||
defaultValues: {
|
||||
name: frameworkControl?.name ?? "",
|
||||
description: frameworkControl?.description ?? "",
|
||||
sectionTitle: frameworkControl?.sectionTitle ?? "",
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = handleSubmit(async (data) => {
|
||||
if (frameworkControl) {
|
||||
// Update the control
|
||||
await mutate({
|
||||
variables: {
|
||||
input: {
|
||||
id: frameworkControl.id,
|
||||
name: data.name,
|
||||
description: data.description,
|
||||
sectionTitle: data.sectionTitle,
|
||||
},
|
||||
},
|
||||
});
|
||||
} else {
|
||||
// Create a new control
|
||||
await mutate({
|
||||
variables: {
|
||||
input: {
|
||||
frameworkId: props.frameworkId,
|
||||
name: data.name,
|
||||
description: data.description,
|
||||
sectionTitle: data.sectionTitle,
|
||||
},
|
||||
connections: [props.connectionId!],
|
||||
},
|
||||
});
|
||||
}
|
||||
dialogRef.current?.close();
|
||||
});
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
trigger={props.children}
|
||||
ref={dialogRef}
|
||||
title={
|
||||
<Breadcrumb
|
||||
items={[
|
||||
__("Controls"),
|
||||
control ? __("Edit Control") : __("New Control"),
|
||||
]}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<form onSubmit={onSubmit}>
|
||||
<DialogContent padded className="space-y-2">
|
||||
<Input
|
||||
id="sectionTitle"
|
||||
required
|
||||
variant="ghost"
|
||||
placeholder={__("Section title")}
|
||||
{...register("sectionTitle")}
|
||||
/>
|
||||
<Input
|
||||
id="title"
|
||||
required
|
||||
variant="title"
|
||||
placeholder={__("Document title")}
|
||||
{...register("name")}
|
||||
/>
|
||||
<Textarea
|
||||
id="content"
|
||||
variant="ghost"
|
||||
required
|
||||
autogrow
|
||||
placeholder={__("Add description")}
|
||||
{...register("description")}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogFooter>
|
||||
<Button type="submit" disabled={isMutating}>
|
||||
{props.control ? __("Update control") : __("Create control")}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
/**
|
||||
* @generated SignedSource<<3a14264d0b2b45f62b29aaefcf7e2c7f>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
import { FragmentRefs } from "relay-runtime";
|
||||
export type CreateControlInput = {
|
||||
description: string;
|
||||
frameworkId: string;
|
||||
name: string;
|
||||
sectionTitle: string;
|
||||
};
|
||||
export type FrameworkControlDialogCreateMutation$variables = {
|
||||
connections: ReadonlyArray<string>;
|
||||
input: CreateControlInput;
|
||||
};
|
||||
export type FrameworkControlDialogCreateMutation$data = {
|
||||
readonly createControl: {
|
||||
readonly controlEdge: {
|
||||
readonly node: {
|
||||
readonly " $fragmentSpreads": FragmentRefs<"FrameworkControlDialogFragment">;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
export type FrameworkControlDialogCreateMutation = {
|
||||
response: FrameworkControlDialogCreateMutation$data;
|
||||
variables: FrameworkControlDialogCreateMutation$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"
|
||||
}
|
||||
];
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": [
|
||||
(v0/*: any*/),
|
||||
(v1/*: any*/)
|
||||
],
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "FrameworkControlDialogCreateMutation",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v2/*: any*/),
|
||||
"concreteType": "CreateControlPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "createControl",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "ControlEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "controlEdge",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Control",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"args": null,
|
||||
"kind": "FragmentSpread",
|
||||
"name": "FrameworkControlDialogFragment"
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": [
|
||||
(v1/*: any*/),
|
||||
(v0/*: any*/)
|
||||
],
|
||||
"kind": "Operation",
|
||||
"name": "FrameworkControlDialogCreateMutation",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v2/*: any*/),
|
||||
"concreteType": "CreateControlPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "createControl",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "ControlEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "controlEdge",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Control",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "name",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "description",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "sectionTitle",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"filters": null,
|
||||
"handle": "prependEdge",
|
||||
"key": "",
|
||||
"kind": "LinkedHandle",
|
||||
"name": "controlEdge",
|
||||
"handleArgs": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "connections",
|
||||
"variableName": "connections"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "87e5bd454a1adfe2f67ecb006e21e115",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "FrameworkControlDialogCreateMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation FrameworkControlDialogCreateMutation(\n $input: CreateControlInput!\n) {\n createControl(input: $input) {\n controlEdge {\n node {\n ...FrameworkControlDialogFragment\n id\n }\n }\n }\n}\n\nfragment FrameworkControlDialogFragment on Control {\n id\n name\n description\n sectionTitle\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "a64e6fd936555c0dd2cb4148cde736b6";
|
||||
|
||||
export default node;
|
||||
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* @generated SignedSource<<6052cc5cfc8407e61eaa1a842e976a90>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ReaderFragment } from 'relay-runtime';
|
||||
import { FragmentRefs } from "relay-runtime";
|
||||
export type FrameworkControlDialogFragment$data = {
|
||||
readonly description: string;
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly sectionTitle: string;
|
||||
readonly " $fragmentType": "FrameworkControlDialogFragment";
|
||||
};
|
||||
export type FrameworkControlDialogFragment$key = {
|
||||
readonly " $data"?: FrameworkControlDialogFragment$data;
|
||||
readonly " $fragmentSpreads": FragmentRefs<"FrameworkControlDialogFragment">;
|
||||
};
|
||||
|
||||
const node: ReaderFragment = {
|
||||
"argumentDefinitions": [],
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "FrameworkControlDialogFragment",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "name",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "description",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "sectionTitle",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Control",
|
||||
"abstractKey": null
|
||||
};
|
||||
|
||||
(node as any).hash = "ff1a543d6a2225d9bacfe7db3b142f1a";
|
||||
|
||||
export default node;
|
||||
@@ -0,0 +1,158 @@
|
||||
/**
|
||||
* @generated SignedSource<<250267db99194edcb6fa20053d1d82df>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
import { FragmentRefs } from "relay-runtime";
|
||||
export type UpdateControlInput = {
|
||||
description?: string | null | undefined;
|
||||
id: string;
|
||||
name?: string | null | undefined;
|
||||
sectionTitle?: string | null | undefined;
|
||||
};
|
||||
export type FrameworkControlDialogUpdateMutation$variables = {
|
||||
input: UpdateControlInput;
|
||||
};
|
||||
export type FrameworkControlDialogUpdateMutation$data = {
|
||||
readonly updateControl: {
|
||||
readonly control: {
|
||||
readonly " $fragmentSpreads": FragmentRefs<"FrameworkControlDialogFragment">;
|
||||
};
|
||||
};
|
||||
};
|
||||
export type FrameworkControlDialogUpdateMutation = {
|
||||
response: FrameworkControlDialogUpdateMutation$data;
|
||||
variables: FrameworkControlDialogUpdateMutation$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "input"
|
||||
}
|
||||
],
|
||||
v1 = [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "input",
|
||||
"variableName": "input"
|
||||
}
|
||||
];
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "FrameworkControlDialogUpdateMutation",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v1/*: any*/),
|
||||
"concreteType": "UpdateControlPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "updateControl",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Control",
|
||||
"kind": "LinkedField",
|
||||
"name": "control",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"args": null,
|
||||
"kind": "FragmentSpread",
|
||||
"name": "FrameworkControlDialogFragment"
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "FrameworkControlDialogUpdateMutation",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v1/*: any*/),
|
||||
"concreteType": "UpdateControlPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "updateControl",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Control",
|
||||
"kind": "LinkedField",
|
||||
"name": "control",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "name",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "description",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "sectionTitle",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "442b248ff35d4448c66052906ea5fff1",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "FrameworkControlDialogUpdateMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation FrameworkControlDialogUpdateMutation(\n $input: UpdateControlInput!\n) {\n updateControl(input: $input) {\n control {\n ...FrameworkControlDialogFragment\n id\n }\n }\n}\n\nfragment FrameworkControlDialogFragment on Control {\n id\n name\n description\n sectionTitle\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "0f070ad6868861c25e409d3777c247dd";
|
||||
|
||||
export default node;
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
Thead,
|
||||
Tr,
|
||||
useConfirm,
|
||||
useDialogRef,
|
||||
} from "@probo/ui";
|
||||
import {
|
||||
measuresQuery,
|
||||
@@ -280,8 +281,11 @@ function MeasureRow(props: MeasureRowProps) {
|
||||
);
|
||||
};
|
||||
|
||||
const dialogRef = useDialogRef();
|
||||
|
||||
return (
|
||||
<>
|
||||
<MeasureFormDialog measure={props.measure} ref={dialogRef} />
|
||||
<Tr to={`/organizations/${organizationId}/measures/${props.measure.id}`}>
|
||||
<Td>{props.measure.name}</Td>
|
||||
<Td width={120}>
|
||||
@@ -289,7 +293,12 @@ function MeasureRow(props: MeasureRowProps) {
|
||||
</Td>
|
||||
<Td noLink width={50} className="text-end">
|
||||
<ActionDropdown>
|
||||
<DropdownItem icon={IconPencil}>{__("Edit")}</DropdownItem>
|
||||
<DropdownItem
|
||||
icon={IconPencil}
|
||||
onClick={() => dialogRef.current?.open()}
|
||||
>
|
||||
{__("Edit")}
|
||||
</DropdownItem>
|
||||
<DropdownItem
|
||||
onClick={onDelete}
|
||||
disabled={isDeleting}
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
PropertyRow,
|
||||
Textarea,
|
||||
useDialogRef,
|
||||
type DialogRef,
|
||||
} from "@probo/ui";
|
||||
import type { ReactNode } from "react";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
@@ -24,12 +25,6 @@ import { useMutationWithToasts } from "/hooks/useMutationWithToasts";
|
||||
import { useOrganizationId } from "/hooks/useOrganizationId";
|
||||
import { useUpdateMeasure } from "/hooks/graph/MeasureGraph";
|
||||
|
||||
type Props = {
|
||||
children: ReactNode;
|
||||
measure?: MeasureFormDialogMeasureFragment$key;
|
||||
connection?: string;
|
||||
};
|
||||
|
||||
const measureFragment = graphql`
|
||||
fragment MeasureFormDialogMeasureFragment on Measure {
|
||||
id
|
||||
@@ -62,10 +57,17 @@ const measureSchema = z.object({
|
||||
state: z.enum(measureStates),
|
||||
});
|
||||
|
||||
type Props = {
|
||||
children?: ReactNode;
|
||||
measure?: MeasureFormDialogMeasureFragment$key;
|
||||
connection?: string;
|
||||
ref?: DialogRef;
|
||||
};
|
||||
|
||||
export default function MeasureFormDialog(props: Props) {
|
||||
const { __ } = useTranslate();
|
||||
const measure = useFragment(measureFragment, props.measure);
|
||||
const dialogRef = useDialogRef();
|
||||
const dialogRef = props.ref ?? useDialogRef();
|
||||
const organizationId = useOrganizationId();
|
||||
const [mutate] = props.measure
|
||||
? useUpdateMeasure()
|
||||
@@ -135,7 +137,7 @@ export default function MeasureFormDialog(props: Props) {
|
||||
id="title"
|
||||
required
|
||||
variant="title"
|
||||
placeholder={__("Document title")}
|
||||
placeholder={__("Measure title")}
|
||||
{...register("name")}
|
||||
/>
|
||||
<Textarea
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import { graphql } from "relay-runtime";
|
||||
import type { MeasureTasksTabFragment$key } from "./__generated__/MeasureTasksTabFragment.graphql";
|
||||
import { useOutletContext } from "react-router";
|
||||
import { useFragment } from "react-relay";
|
||||
import TasksCard from "/components/tasks/TasksCard";
|
||||
import { Button, IconPlusLarge } from "@probo/ui";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import TaskFormDialog from "/components/tasks/TaskFormDialog";
|
||||
|
||||
const tasksFragment = graphql`
|
||||
fragment MeasureTasksTabFragment on Measure {
|
||||
tasks(first: 100) @connection(key: "Measure__tasks") {
|
||||
__id
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
name
|
||||
state
|
||||
description
|
||||
...TaskFormDialogFragment
|
||||
assignedTo {
|
||||
id
|
||||
fullName
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default function MeasureTasksTab() {
|
||||
const { __ } = useTranslate();
|
||||
const { measure } = useOutletContext<{
|
||||
measure: MeasureTasksTabFragment$key & { id: string };
|
||||
}>();
|
||||
const data = useFragment(tasksFragment, measure);
|
||||
const connectionId = data.tasks.__id;
|
||||
const tasks = data.tasks?.edges?.map((edge) => edge.node) ?? [];
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<TasksCard connectionId={connectionId} tasks={tasks} />
|
||||
<TaskFormDialog connection={connectionId} measureId={measure.id}>
|
||||
<Button
|
||||
variant="secondary"
|
||||
icon={IconPlusLarge}
|
||||
className="absolute top-3 right-6"
|
||||
>
|
||||
{__("New task")}
|
||||
</Button>
|
||||
</TaskFormDialog>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
201
apps/console2/src/pages/organizations/measures/tabs/__generated__/MeasureTasksTabFragment.graphql.ts
generated
Normal file
201
apps/console2/src/pages/organizations/measures/tabs/__generated__/MeasureTasksTabFragment.graphql.ts
generated
Normal file
@@ -0,0 +1,201 @@
|
||||
/**
|
||||
* @generated SignedSource<<67fd0b26613189fa036f7873138787d7>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ReaderFragment } from 'relay-runtime';
|
||||
export type TaskState = "DONE" | "TODO";
|
||||
import { FragmentRefs } from "relay-runtime";
|
||||
export type MeasureTasksTabFragment$data = {
|
||||
readonly tasks: {
|
||||
readonly __id: string;
|
||||
readonly edges: ReadonlyArray<{
|
||||
readonly node: {
|
||||
readonly assignedTo: {
|
||||
readonly fullName: string;
|
||||
readonly id: string;
|
||||
} | null | undefined;
|
||||
readonly description: string;
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly state: TaskState;
|
||||
readonly " $fragmentSpreads": FragmentRefs<"TaskFormDialogFragment">;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
readonly " $fragmentType": "MeasureTasksTabFragment";
|
||||
};
|
||||
export type MeasureTasksTabFragment$key = {
|
||||
readonly " $data"?: MeasureTasksTabFragment$data;
|
||||
readonly " $fragmentSpreads": FragmentRefs<"MeasureTasksTabFragment">;
|
||||
};
|
||||
|
||||
const node: ReaderFragment = (function(){
|
||||
var v0 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"argumentDefinitions": [],
|
||||
"kind": "Fragment",
|
||||
"metadata": {
|
||||
"connection": [
|
||||
{
|
||||
"count": null,
|
||||
"cursor": null,
|
||||
"direction": "forward",
|
||||
"path": [
|
||||
"tasks"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "MeasureTasksTabFragment",
|
||||
"selections": [
|
||||
{
|
||||
"alias": "tasks",
|
||||
"args": null,
|
||||
"concreteType": "TaskConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "__Measure__tasks_connection",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "TaskEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "edges",
|
||||
"plural": true,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Task",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v0/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "name",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "state",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "description",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"args": null,
|
||||
"kind": "FragmentSpread",
|
||||
"name": "TaskFormDialogFragment"
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "People",
|
||||
"kind": "LinkedField",
|
||||
"name": "assignedTo",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v0/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "fullName",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"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
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"kind": "ClientExtension",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "__id",
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Measure",
|
||||
"abstractKey": null
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "40d3211a52860f68da7c89ba9123af61";
|
||||
|
||||
export default node;
|
||||
86
apps/console2/src/pages/organizations/tasks/TasksPage.tsx
Normal file
86
apps/console2/src/pages/organizations/tasks/TasksPage.tsx
Normal file
@@ -0,0 +1,86 @@
|
||||
import { Button, IconPlusLarge, PageHeader } from "@probo/ui";
|
||||
import {
|
||||
usePreloadedQuery,
|
||||
useRefetchableFragment,
|
||||
type PreloadedQuery,
|
||||
} from "react-relay";
|
||||
import { graphql } from "relay-runtime";
|
||||
import type { TaskGraphQuery } from "/hooks/graph/__generated__/TaskGraphQuery.graphql";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import type { TasksPageFragment$key } from "./__generated__/TasksPageFragment.graphql";
|
||||
import { tasksQuery } from "/hooks/graph/TaskGraph";
|
||||
import { usePageTitle } from "@probo/hooks";
|
||||
import TasksCard from "/components/tasks/TasksCard";
|
||||
import TaskFormDialog from "/components/tasks/TaskFormDialog";
|
||||
|
||||
const tasksFragment = graphql`
|
||||
fragment TasksPageFragment on Organization
|
||||
@refetchable(queryName: "TasksPageFragment_query")
|
||||
@argumentDefinitions(
|
||||
first: { type: "Int", defaultValue: 20 }
|
||||
order: { type: "TaskOrder", defaultValue: null }
|
||||
after: { type: "CursorKey", defaultValue: null }
|
||||
before: { type: "CursorKey", defaultValue: null }
|
||||
last: { type: "Int", defaultValue: null }
|
||||
) {
|
||||
tasks(
|
||||
first: $first
|
||||
after: $after
|
||||
last: $last
|
||||
before: $before
|
||||
orderBy: $order
|
||||
) @connection(key: "TasksPageFragment_tasks") {
|
||||
__id
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
name
|
||||
state
|
||||
description
|
||||
...TaskFormDialogFragment
|
||||
measure {
|
||||
id
|
||||
name
|
||||
}
|
||||
assignedTo {
|
||||
id
|
||||
fullName
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
queryRef: PreloadedQuery<TaskGraphQuery>;
|
||||
}
|
||||
|
||||
export default function TasksPage({ queryRef }: Props) {
|
||||
const { __ } = useTranslate();
|
||||
const query = usePreloadedQuery(tasksQuery, queryRef);
|
||||
const [data] = useRefetchableFragment(
|
||||
tasksFragment,
|
||||
query.organization as TasksPageFragment$key
|
||||
);
|
||||
const tasks = data.tasks?.edges.map((edge) => edge.node);
|
||||
const connectionId = data.tasks.__id;
|
||||
|
||||
usePageTitle(__("Tasks"));
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<PageHeader
|
||||
title={__("Tasks")}
|
||||
description={__(
|
||||
"Track your assigned compliance tasks and keep progress on track."
|
||||
)}
|
||||
>
|
||||
<TaskFormDialog connection={connectionId}>
|
||||
<Button icon={IconPlusLarge}>{__("New task")}</Button>
|
||||
</TaskFormDialog>
|
||||
</PageHeader>
|
||||
<TasksCard connectionId={connectionId} tasks={tasks ?? []} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
291
apps/console2/src/pages/organizations/tasks/__generated__/TasksPageFragment.graphql.ts
generated
Normal file
291
apps/console2/src/pages/organizations/tasks/__generated__/TasksPageFragment.graphql.ts
generated
Normal file
@@ -0,0 +1,291 @@
|
||||
/**
|
||||
* @generated SignedSource<<24b80331a26f03ece71f38e531454fe2>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ReaderFragment } from 'relay-runtime';
|
||||
export type TaskState = "DONE" | "TODO";
|
||||
import { FragmentRefs } from "relay-runtime";
|
||||
export type TasksPageFragment$data = {
|
||||
readonly id: string;
|
||||
readonly tasks: {
|
||||
readonly __id: string;
|
||||
readonly edges: ReadonlyArray<{
|
||||
readonly node: {
|
||||
readonly assignedTo: {
|
||||
readonly fullName: string;
|
||||
readonly id: string;
|
||||
} | null | undefined;
|
||||
readonly description: string;
|
||||
readonly id: string;
|
||||
readonly measure: {
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
} | null | undefined;
|
||||
readonly name: string;
|
||||
readonly state: TaskState;
|
||||
readonly " $fragmentSpreads": FragmentRefs<"TaskFormDialogFragment">;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
readonly " $fragmentType": "TasksPageFragment";
|
||||
};
|
||||
export type TasksPageFragment$key = {
|
||||
readonly " $data"?: TasksPageFragment$data;
|
||||
readonly " $fragmentSpreads": FragmentRefs<"TasksPageFragment">;
|
||||
};
|
||||
|
||||
import TasksPageFragment_query_graphql from './TasksPageFragment_query.graphql';
|
||||
|
||||
const node: ReaderFragment = (function(){
|
||||
var v0 = [
|
||||
"tasks"
|
||||
],
|
||||
v1 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
v2 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "name",
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"argumentDefinitions": [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "after"
|
||||
},
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "before"
|
||||
},
|
||||
{
|
||||
"defaultValue": 20,
|
||||
"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": TasksPageFragment_query_graphql,
|
||||
"identifierInfo": {
|
||||
"identifierField": "id",
|
||||
"identifierQueryVariableName": "id"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "TasksPageFragment",
|
||||
"selections": [
|
||||
{
|
||||
"alias": "tasks",
|
||||
"args": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "orderBy",
|
||||
"variableName": "order"
|
||||
}
|
||||
],
|
||||
"concreteType": "TaskConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "__TasksPageFragment_tasks_connection",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "TaskEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "edges",
|
||||
"plural": true,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Task",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v1/*: any*/),
|
||||
(v2/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "state",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "description",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"args": null,
|
||||
"kind": "FragmentSpread",
|
||||
"name": "TaskFormDialogFragment"
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Measure",
|
||||
"kind": "LinkedField",
|
||||
"name": "measure",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v1/*: any*/),
|
||||
(v2/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "People",
|
||||
"kind": "LinkedField",
|
||||
"name": "assignedTo",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v1/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "fullName",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"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 = "a555e698539fe819dd4713ac507ea440";
|
||||
|
||||
export default node;
|
||||
384
apps/console2/src/pages/organizations/tasks/__generated__/TasksPageFragment_query.graphql.ts
generated
Normal file
384
apps/console2/src/pages/organizations/tasks/__generated__/TasksPageFragment_query.graphql.ts
generated
Normal file
@@ -0,0 +1,384 @@
|
||||
/**
|
||||
* @generated SignedSource<<fcc24a794a473d9c17436612840d3228>>
|
||||
* @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 TaskOrderField = "CREATED_AT";
|
||||
export type TaskOrder = {
|
||||
direction: OrderDirection;
|
||||
field: TaskOrderField;
|
||||
};
|
||||
export type TasksPageFragment_query$variables = {
|
||||
after?: any | null | undefined;
|
||||
before?: any | null | undefined;
|
||||
first?: number | null | undefined;
|
||||
id: string;
|
||||
last?: number | null | undefined;
|
||||
order?: TaskOrder | null | undefined;
|
||||
};
|
||||
export type TasksPageFragment_query$data = {
|
||||
readonly node: {
|
||||
readonly " $fragmentSpreads": FragmentRefs<"TasksPageFragment">;
|
||||
};
|
||||
};
|
||||
export type TasksPageFragment_query = {
|
||||
response: TasksPageFragment_query$data;
|
||||
variables: TasksPageFragment_query$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = {
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "after"
|
||||
},
|
||||
v1 = {
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "before"
|
||||
},
|
||||
v2 = {
|
||||
"defaultValue": 20,
|
||||
"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"
|
||||
}
|
||||
],
|
||||
v14 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "name",
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": [
|
||||
(v0/*: any*/),
|
||||
(v1/*: any*/),
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/),
|
||||
(v4/*: any*/),
|
||||
(v5/*: any*/)
|
||||
],
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "TasksPageFragment_query",
|
||||
"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": "TasksPageFragment"
|
||||
}
|
||||
],
|
||||
"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": "TasksPageFragment_query",
|
||||
"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": "TaskConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "tasks",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "TaskEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "edges",
|
||||
"plural": true,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Task",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v12/*: any*/),
|
||||
(v14/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "state",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "description",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "timeEstimate",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "deadline",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "People",
|
||||
"kind": "LinkedField",
|
||||
"name": "assignedTo",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v12/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "fullName",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Measure",
|
||||
"kind": "LinkedField",
|
||||
"name": "measure",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v12/*: any*/),
|
||||
(v14/*: any*/)
|
||||
],
|
||||
"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": "TasksPageFragment_tasks",
|
||||
"kind": "LinkedHandle",
|
||||
"name": "tasks"
|
||||
}
|
||||
],
|
||||
"type": "Organization",
|
||||
"abstractKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "9f6192e72e8d38b3150dfc8ece83ad97",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "TasksPageFragment_query",
|
||||
"operationKind": "query",
|
||||
"text": "query TasksPageFragment_query(\n $after: CursorKey = null\n $before: CursorKey = null\n $first: Int = 20\n $last: Int = null\n $order: TaskOrder = null\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...TasksPageFragment_16fISc\n id\n }\n}\n\nfragment TaskFormDialogFragment on Task {\n id\n description\n name\n state\n timeEstimate\n deadline\n assignedTo {\n id\n }\n measure {\n id\n }\n}\n\nfragment TasksPageFragment_16fISc on Organization {\n tasks(first: $first, after: $after, last: $last, before: $before, orderBy: $order) {\n edges {\n node {\n id\n name\n state\n description\n ...TaskFormDialogFragment\n measure {\n id\n name\n }\n assignedTo {\n id\n fullName\n }\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 = "a555e698539fe819dd4713ac507ea440";
|
||||
|
||||
export default node;
|
||||
@@ -23,6 +23,7 @@ import { organizationViewQuery } from "./hooks/graph/OrganizationGraph.ts";
|
||||
import { peopleRoutes } from "./routes/peopleRoutes.ts";
|
||||
import { frameworkRoutes } from "./routes/frameworkRoutes.ts";
|
||||
import { PageError } from "./components/PageError.tsx";
|
||||
import { taskRoutes } from "./routes/taskRoutes.ts";
|
||||
|
||||
function ErrorBoundary() {
|
||||
const error = useRouteError();
|
||||
@@ -88,6 +89,7 @@ const routes = [
|
||||
...peopleRoutes,
|
||||
...vendorRoutes,
|
||||
...frameworkRoutes,
|
||||
...taskRoutes,
|
||||
{
|
||||
path: "*",
|
||||
Component: PageError,
|
||||
|
||||
@@ -42,6 +42,12 @@ export const measureRoutes = [
|
||||
() => import("/pages/organizations/measures/tabs/MeasureRisksTab.tsx")
|
||||
),
|
||||
},
|
||||
{
|
||||
path: "tasks",
|
||||
Component: lazy(
|
||||
() => import("/pages/organizations/measures/tabs/MeasureTasksTab.tsx")
|
||||
),
|
||||
},
|
||||
{
|
||||
path: "controls",
|
||||
Component: lazy(
|
||||
|
||||
18
apps/console2/src/routes/taskRoutes.ts
Normal file
18
apps/console2/src/routes/taskRoutes.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { lazy } from "react";
|
||||
import { PageSkeleton } from "/components/skeletons/PageSkeleton";
|
||||
import { loadQuery } from "react-relay";
|
||||
import { relayEnvironment } from "/providers/RelayProviders";
|
||||
import { tasksQuery } from "/hooks/graph/TaskGraph";
|
||||
import type { AppRoute } from "/routes.tsx";
|
||||
|
||||
export const taskRoutes = [
|
||||
{
|
||||
path: "tasks",
|
||||
fallback: PageSkeleton,
|
||||
queryLoader: ({ organizationId }) =>
|
||||
loadQuery(relayEnvironment, tasksQuery, {
|
||||
organizationId,
|
||||
}),
|
||||
Component: lazy(() => import("/pages/organizations/tasks/TasksPage")),
|
||||
},
|
||||
] satisfies AppRoute[];
|
||||
@@ -6,7 +6,7 @@ type Props = {
|
||||
active?: boolean;
|
||||
id: string;
|
||||
description?: string;
|
||||
to: string;
|
||||
to?: string;
|
||||
} & HTMLAttributes<HTMLAnchorElement>;
|
||||
|
||||
const classNames = tv({
|
||||
@@ -40,10 +40,12 @@ export function ControlItem({ active, id, description, to, ...props }: Props) {
|
||||
} = classNames({
|
||||
active,
|
||||
});
|
||||
return (
|
||||
<Link className={wrapper()} to={to} {...props}>
|
||||
<div className={idCls()}>{id}</div>
|
||||
<div className={descriptionCls()}>{description}</div>
|
||||
</Link>
|
||||
);
|
||||
if (to) {
|
||||
return (
|
||||
<Link className={wrapper()} to={to} {...props}>
|
||||
<div className={idCls()}>{id}</div>
|
||||
<div className={descriptionCls()}>{description}</div>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import type {IconProps} from "./type.ts";
|
||||
|
||||
export function IconCircleProgress({size = 24, className}: IconProps) {
|
||||
return <svg width={size} height={size} viewBox="0 0 24 24" fill="currentColor" className={className} xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.2686 20.7061C10.6885 20.7891 11.1193 20.8427 11.5586 20.8643L12 20.875C12.5791 20.875 13.1455 20.8177 13.6934 20.7119L13.6943 20.7129L13.7578 20.7031C13.826 20.6987 13.8906 20.7327 13.9268 20.7891L13.9531 20.8535C13.9727 20.9524 13.9096 21.0482 13.8115 21.0713L13.3535 21.1523C12.9126 21.217 12.4603 21.25 12 21.25L11.54 21.2383C11.2349 21.2233 10.9335 21.1942 10.6367 21.1504L10.1953 21.0742C10.1187 21.059 10.0627 20.9991 10.0479 20.9277L10.0469 20.8535C10.0645 20.7649 10.1431 20.7027 10.2305 20.7021L10.2686 20.7061ZM5.06348 17.5361C5.54244 18.1352 6.09709 18.6714 6.71289 19.1289L6.72656 19.1387L7.03809 19.3594H7.03906L7.08594 19.3936C7.13994 19.4388 7.16311 19.5104 7.14844 19.5771L7.12109 19.6406C7.06844 19.7193 6.96543 19.745 6.88281 19.7041L6.84863 19.6836L6.47559 19.4189C5.83054 18.9378 5.25075 18.3745 4.75098 17.7451L5.06348 17.5361ZM19.6074 16.8623L19.6406 16.8799C19.7052 16.9232 19.7346 16.9996 19.7207 17.0713L19.6924 17.1396C19.1565 17.94 18.4968 18.6497 17.7441 19.2471L17.5352 18.9346C18.1339 18.456 18.6714 17.9039 19.1289 17.2881L19.3799 16.9316H19.3809C19.4311 16.8566 19.5264 16.8291 19.6074 16.8623ZM12 7.125C14.6924 7.125 16.8749 9.30763 16.875 12C16.875 14.6924 14.6924 16.875 12 16.875C9.30763 16.8749 7.125 14.6924 7.125 12C7.12508 9.30768 9.30768 7.12508 12 7.125ZM3.14648 10.0469C3.23507 10.0645 3.29728 10.1429 3.29785 10.2305L3.29395 10.2686C3.18327 10.8282 3.12503 11.4073 3.125 12L3.13086 12.332C3.14306 12.6626 3.17342 12.9885 3.2207 13.3086L3.22266 13.3174L3.22363 13.3262L3.28711 13.6934V13.6943L3.2959 13.7578C3.30033 13.8262 3.26726 13.8907 3.21094 13.9268L3.14648 13.9531C3.05242 13.9717 2.96029 13.9159 2.93164 13.8281L2.9248 13.7998L2.84863 13.3574C2.78348 12.9144 2.75001 12.4608 2.75 12L2.76172 11.54C2.7767 11.2349 2.80575 10.9335 2.84961 10.6367L2.92578 10.1953C2.94092 10.1189 3.00077 10.0627 3.07227 10.0479L3.14648 10.0469ZM21.1865 10.9365C21.2265 11.2857 21.25 11.6404 21.25 12L21.2383 12.4609C21.2161 12.9128 21.162 13.3563 21.0771 13.7891L21.0674 13.8281C21.0459 13.8936 20.9901 13.941 20.9238 13.9531H20.8535C20.7518 13.9328 20.6861 13.8336 20.7061 13.7324L20.708 13.7246L20.709 13.7158L20.7764 13.3281L20.7803 13.3086C20.8434 12.8809 20.875 12.4435 20.875 12L20.8643 11.5586C20.8552 11.374 20.8375 11.1912 20.8174 11.0098L21.1865 10.9365ZM6.88086 4.2959C6.94273 4.26456 7.01679 4.27193 7.07227 4.31055L7.12109 4.36035C7.17773 4.44575 7.15476 4.56129 7.06836 4.61914L7.05273 4.63086L6.72656 4.86133L6.71289 4.87109C5.99852 5.40185 5.3658 6.03774 4.83887 6.75586L4.63672 7.04395L4.60254 7.08984C4.54208 7.15747 4.43818 7.1738 4.35938 7.12109H4.36035C4.29528 7.07753 4.26556 7.00042 4.2793 6.92871L4.30762 6.86035L4.58105 6.47559L4.58008 6.47461C5.2073 5.63366 5.97424 4.90321 6.8457 4.31738L6.88086 4.2959ZM17.1396 4.30762C18.0169 4.89492 18.7883 5.62923 19.4189 6.47461V6.47559L19.6836 6.84863L19.7041 6.88184C19.7351 6.94366 19.7288 7.01698 19.6904 7.07227L19.6406 7.12109C19.5609 7.17429 19.4538 7.15783 19.3936 7.08594L19.3594 7.03906L19.1387 6.72656L19.1289 6.71289L18.8945 6.41211C18.4143 5.82018 17.8598 5.2906 17.2441 4.83887L16.9316 4.61914C16.8672 4.57602 16.8377 4.50044 16.8516 4.42871L16.8799 4.36035C16.9234 4.29533 16.9998 4.26657 17.0713 4.28027L17.1396 4.30762ZM12 2.75C12.4608 2.75001 12.9144 2.78348 13.3574 2.84863L13.7998 2.9248L13.8281 2.93164C13.8936 2.95302 13.941 3.0097 13.9531 3.07617V3.14648C13.9347 3.23945 13.8495 3.30184 13.7578 3.2959L13.6943 3.28711H13.6934L13.3262 3.22363L13.3174 3.22266L13.3086 3.2207L12.9863 3.17969C12.7705 3.1558 12.5524 3.13899 12.332 3.13086L12 3.125C11.5554 3.12502 11.1183 3.15759 10.6914 3.2207L10.2686 3.29395C10.1921 3.30906 10.1173 3.27531 10.0762 3.21484L10.0469 3.14648C10.0294 3.05808 10.0779 2.97145 10.1582 2.9375L10.1953 2.92578C10.6332 2.83917 11.0824 2.78418 11.54 2.76172L12 2.75Z" fill="currentColor" stroke="#141E12" strokeWidth="1.5"/>
|
||||
<path d="M10.2686 20.7061C10.6885 20.7891 11.1193 20.8427 11.5586 20.8643L12 20.875C12.5791 20.875 13.1455 20.8177 13.6934 20.7119L13.6943 20.7129L13.7578 20.7031C13.826 20.6987 13.8906 20.7327 13.9268 20.7891L13.9531 20.8535C13.9727 20.9524 13.9096 21.0482 13.8115 21.0713L13.3535 21.1523C12.9126 21.217 12.4603 21.25 12 21.25L11.54 21.2383C11.2349 21.2233 10.9335 21.1942 10.6367 21.1504L10.1953 21.0742C10.1187 21.059 10.0627 20.9991 10.0479 20.9277L10.0469 20.8535C10.0645 20.7649 10.1431 20.7027 10.2305 20.7021L10.2686 20.7061ZM5.06348 17.5361C5.54244 18.1352 6.09709 18.6714 6.71289 19.1289L6.72656 19.1387L7.03809 19.3594H7.03906L7.08594 19.3936C7.13994 19.4388 7.16311 19.5104 7.14844 19.5771L7.12109 19.6406C7.06844 19.7193 6.96543 19.745 6.88281 19.7041L6.84863 19.6836L6.47559 19.4189C5.83054 18.9378 5.25075 18.3745 4.75098 17.7451L5.06348 17.5361ZM19.6074 16.8623L19.6406 16.8799C19.7052 16.9232 19.7346 16.9996 19.7207 17.0713L19.6924 17.1396C19.1565 17.94 18.4968 18.6497 17.7441 19.2471L17.5352 18.9346C18.1339 18.456 18.6714 17.9039 19.1289 17.2881L19.3799 16.9316H19.3809C19.4311 16.8566 19.5264 16.8291 19.6074 16.8623ZM12 7.125C14.6924 7.125 16.8749 9.30763 16.875 12C16.875 14.6924 14.6924 16.875 12 16.875C9.30763 16.8749 7.125 14.6924 7.125 12C7.12508 9.30768 9.30768 7.12508 12 7.125ZM3.14648 10.0469C3.23507 10.0645 3.29728 10.1429 3.29785 10.2305L3.29395 10.2686C3.18327 10.8282 3.12503 11.4073 3.125 12L3.13086 12.332C3.14306 12.6626 3.17342 12.9885 3.2207 13.3086L3.22266 13.3174L3.22363 13.3262L3.28711 13.6934V13.6943L3.2959 13.7578C3.30033 13.8262 3.26726 13.8907 3.21094 13.9268L3.14648 13.9531C3.05242 13.9717 2.96029 13.9159 2.93164 13.8281L2.9248 13.7998L2.84863 13.3574C2.78348 12.9144 2.75001 12.4608 2.75 12L2.76172 11.54C2.7767 11.2349 2.80575 10.9335 2.84961 10.6367L2.92578 10.1953C2.94092 10.1189 3.00077 10.0627 3.07227 10.0479L3.14648 10.0469ZM21.1865 10.9365C21.2265 11.2857 21.25 11.6404 21.25 12L21.2383 12.4609C21.2161 12.9128 21.162 13.3563 21.0771 13.7891L21.0674 13.8281C21.0459 13.8936 20.9901 13.941 20.9238 13.9531H20.8535C20.7518 13.9328 20.6861 13.8336 20.7061 13.7324L20.708 13.7246L20.709 13.7158L20.7764 13.3281L20.7803 13.3086C20.8434 12.8809 20.875 12.4435 20.875 12L20.8643 11.5586C20.8552 11.374 20.8375 11.1912 20.8174 11.0098L21.1865 10.9365ZM6.88086 4.2959C6.94273 4.26456 7.01679 4.27193 7.07227 4.31055L7.12109 4.36035C7.17773 4.44575 7.15476 4.56129 7.06836 4.61914L7.05273 4.63086L6.72656 4.86133L6.71289 4.87109C5.99852 5.40185 5.3658 6.03774 4.83887 6.75586L4.63672 7.04395L4.60254 7.08984C4.54208 7.15747 4.43818 7.1738 4.35938 7.12109H4.36035C4.29528 7.07753 4.26556 7.00042 4.2793 6.92871L4.30762 6.86035L4.58105 6.47559L4.58008 6.47461C5.2073 5.63366 5.97424 4.90321 6.8457 4.31738L6.88086 4.2959ZM17.1396 4.30762C18.0169 4.89492 18.7883 5.62923 19.4189 6.47461V6.47559L19.6836 6.84863L19.7041 6.88184C19.7351 6.94366 19.7288 7.01698 19.6904 7.07227L19.6406 7.12109C19.5609 7.17429 19.4538 7.15783 19.3936 7.08594L19.3594 7.03906L19.1387 6.72656L19.1289 6.71289L18.8945 6.41211C18.4143 5.82018 17.8598 5.2906 17.2441 4.83887L16.9316 4.61914C16.8672 4.57602 16.8377 4.50044 16.8516 4.42871L16.8799 4.36035C16.9234 4.29533 16.9998 4.26657 17.0713 4.28027L17.1396 4.30762ZM12 2.75C12.4608 2.75001 12.9144 2.78348 13.3574 2.84863L13.7998 2.9248L13.8281 2.93164C13.8936 2.95302 13.941 3.0097 13.9531 3.07617V3.14648C13.9347 3.23945 13.8495 3.30184 13.7578 3.2959L13.6943 3.28711H13.6934L13.3262 3.22363L13.3174 3.22266L13.3086 3.2207L12.9863 3.17969C12.7705 3.1558 12.5524 3.13899 12.332 3.13086L12 3.125C11.5554 3.12502 11.1183 3.15759 10.6914 3.2207L10.2686 3.29395C10.1921 3.30906 10.1173 3.27531 10.0762 3.21484L10.0469 3.14648C10.0294 3.05808 10.0779 2.97145 10.1582 2.9375L10.1953 2.92578C10.6332 2.83917 11.0824 2.78418 11.54 2.76172L12 2.75Z" fill="currentColor" stroke="currentColor" strokeWidth="1.5"/>
|
||||
</svg>
|
||||
|
||||
}
|
||||
22
packages/ui/src/Atoms/Icons/TaskStateIcon.stories.tsx
Normal file
22
packages/ui/src/Atoms/Icons/TaskStateIcon.stories.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import { TaskStateIcon } from "./TaskStateIcon";
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
|
||||
export default {
|
||||
title: "Atoms/TaskStateIcon",
|
||||
component: TaskStateIcon,
|
||||
argTypes: {},
|
||||
} satisfies Meta<typeof TaskStateIcon>;
|
||||
|
||||
type Story = StoryObj<typeof TaskStateIcon>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
state: "TODO",
|
||||
},
|
||||
};
|
||||
|
||||
export const Done: Story = {
|
||||
args: {
|
||||
state: "DONE",
|
||||
},
|
||||
};
|
||||
14
packages/ui/src/Atoms/Icons/TaskStateIcon.tsx
Normal file
14
packages/ui/src/Atoms/Icons/TaskStateIcon.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { IconCircleProgress } from "./IconCircleProgress";
|
||||
import { IconRadioUnchecked } from "./IconRadioUnchecked";
|
||||
|
||||
type Props = {
|
||||
state: "TODO" | "DONE";
|
||||
};
|
||||
|
||||
export function TaskStateIcon({ state }: Props) {
|
||||
return state === "TODO" ? (
|
||||
<IconRadioUnchecked size={16} className="text-txt-quaternary" />
|
||||
) : (
|
||||
<IconCircleProgress size={16} className="text-txt-accent" />
|
||||
);
|
||||
}
|
||||
@@ -1,3 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.2686 20.7061C10.6885 20.7891 11.1193 20.8427 11.5586 20.8643L12 20.875C12.5791 20.875 13.1455 20.8177 13.6934 20.7119L13.6943 20.7129L13.7578 20.7031C13.826 20.6987 13.8906 20.7327 13.9268 20.7891L13.9531 20.8535C13.9727 20.9524 13.9096 21.0482 13.8115 21.0713L13.3535 21.1523C12.9126 21.217 12.4603 21.25 12 21.25L11.54 21.2383C11.2349 21.2233 10.9335 21.1942 10.6367 21.1504L10.1953 21.0742C10.1187 21.059 10.0627 20.9991 10.0479 20.9277L10.0469 20.8535C10.0645 20.7649 10.1431 20.7027 10.2305 20.7021L10.2686 20.7061ZM5.06348 17.5361C5.54244 18.1352 6.09709 18.6714 6.71289 19.1289L6.72656 19.1387L7.03809 19.3594H7.03906L7.08594 19.3936C7.13994 19.4388 7.16311 19.5104 7.14844 19.5771L7.12109 19.6406C7.06844 19.7193 6.96543 19.745 6.88281 19.7041L6.84863 19.6836L6.47559 19.4189C5.83054 18.9378 5.25075 18.3745 4.75098 17.7451L5.06348 17.5361ZM19.6074 16.8623L19.6406 16.8799C19.7052 16.9232 19.7346 16.9996 19.7207 17.0713L19.6924 17.1396C19.1565 17.94 18.4968 18.6497 17.7441 19.2471L17.5352 18.9346C18.1339 18.456 18.6714 17.9039 19.1289 17.2881L19.3799 16.9316H19.3809C19.4311 16.8566 19.5264 16.8291 19.6074 16.8623ZM12 7.125C14.6924 7.125 16.8749 9.30763 16.875 12C16.875 14.6924 14.6924 16.875 12 16.875C9.30763 16.8749 7.125 14.6924 7.125 12C7.12508 9.30768 9.30768 7.12508 12 7.125ZM3.14648 10.0469C3.23507 10.0645 3.29728 10.1429 3.29785 10.2305L3.29395 10.2686C3.18327 10.8282 3.12503 11.4073 3.125 12L3.13086 12.332C3.14306 12.6626 3.17342 12.9885 3.2207 13.3086L3.22266 13.3174L3.22363 13.3262L3.28711 13.6934V13.6943L3.2959 13.7578C3.30033 13.8262 3.26726 13.8907 3.21094 13.9268L3.14648 13.9531C3.05242 13.9717 2.96029 13.9159 2.93164 13.8281L2.9248 13.7998L2.84863 13.3574C2.78348 12.9144 2.75001 12.4608 2.75 12L2.76172 11.54C2.7767 11.2349 2.80575 10.9335 2.84961 10.6367L2.92578 10.1953C2.94092 10.1189 3.00077 10.0627 3.07227 10.0479L3.14648 10.0469ZM21.1865 10.9365C21.2265 11.2857 21.25 11.6404 21.25 12L21.2383 12.4609C21.2161 12.9128 21.162 13.3563 21.0771 13.7891L21.0674 13.8281C21.0459 13.8936 20.9901 13.941 20.9238 13.9531H20.8535C20.7518 13.9328 20.6861 13.8336 20.7061 13.7324L20.708 13.7246L20.709 13.7158L20.7764 13.3281L20.7803 13.3086C20.8434 12.8809 20.875 12.4435 20.875 12L20.8643 11.5586C20.8552 11.374 20.8375 11.1912 20.8174 11.0098L21.1865 10.9365ZM6.88086 4.2959C6.94273 4.26456 7.01679 4.27193 7.07227 4.31055L7.12109 4.36035C7.17773 4.44575 7.15476 4.56129 7.06836 4.61914L7.05273 4.63086L6.72656 4.86133L6.71289 4.87109C5.99852 5.40185 5.3658 6.03774 4.83887 6.75586L4.63672 7.04395L4.60254 7.08984C4.54208 7.15747 4.43818 7.1738 4.35938 7.12109H4.36035C4.29528 7.07753 4.26556 7.00042 4.2793 6.92871L4.30762 6.86035L4.58105 6.47559L4.58008 6.47461C5.2073 5.63366 5.97424 4.90321 6.8457 4.31738L6.88086 4.2959ZM17.1396 4.30762C18.0169 4.89492 18.7883 5.62923 19.4189 6.47461V6.47559L19.6836 6.84863L19.7041 6.88184C19.7351 6.94366 19.7288 7.01698 19.6904 7.07227L19.6406 7.12109C19.5609 7.17429 19.4538 7.15783 19.3936 7.08594L19.3594 7.03906L19.1387 6.72656L19.1289 6.71289L18.8945 6.41211C18.4143 5.82018 17.8598 5.2906 17.2441 4.83887L16.9316 4.61914C16.8672 4.57602 16.8377 4.50044 16.8516 4.42871L16.8799 4.36035C16.9234 4.29533 16.9998 4.26657 17.0713 4.28027L17.1396 4.30762ZM12 2.75C12.4608 2.75001 12.9144 2.78348 13.3574 2.84863L13.7998 2.9248L13.8281 2.93164C13.8936 2.95302 13.941 3.0097 13.9531 3.07617V3.14648C13.9347 3.23945 13.8495 3.30184 13.7578 3.2959L13.6943 3.28711H13.6934L13.3262 3.22363L13.3174 3.22266L13.3086 3.2207L12.9863 3.17969C12.7705 3.1558 12.5524 3.13899 12.332 3.13086L12 3.125C11.5554 3.12502 11.1183 3.15759 10.6914 3.2207L10.2686 3.29395C10.1921 3.30906 10.1173 3.27531 10.0762 3.21484L10.0469 3.14648C10.0294 3.05808 10.0779 2.97145 10.1582 2.9375L10.1953 2.92578C10.6332 2.83917 11.0824 2.78418 11.54 2.76172L12 2.75Z" fill="#141E12" stroke="#141E12" stroke-width="1.5"/>
|
||||
<path d="M10.2686 20.7061C10.6885 20.7891 11.1193 20.8427 11.5586 20.8643L12 20.875C12.5791 20.875 13.1455 20.8177 13.6934 20.7119L13.6943 20.7129L13.7578 20.7031C13.826 20.6987 13.8906 20.7327 13.9268 20.7891L13.9531 20.8535C13.9727 20.9524 13.9096 21.0482 13.8115 21.0713L13.3535 21.1523C12.9126 21.217 12.4603 21.25 12 21.25L11.54 21.2383C11.2349 21.2233 10.9335 21.1942 10.6367 21.1504L10.1953 21.0742C10.1187 21.059 10.0627 20.9991 10.0479 20.9277L10.0469 20.8535C10.0645 20.7649 10.1431 20.7027 10.2305 20.7021L10.2686 20.7061ZM5.06348 17.5361C5.54244 18.1352 6.09709 18.6714 6.71289 19.1289L6.72656 19.1387L7.03809 19.3594H7.03906L7.08594 19.3936C7.13994 19.4388 7.16311 19.5104 7.14844 19.5771L7.12109 19.6406C7.06844 19.7193 6.96543 19.745 6.88281 19.7041L6.84863 19.6836L6.47559 19.4189C5.83054 18.9378 5.25075 18.3745 4.75098 17.7451L5.06348 17.5361ZM19.6074 16.8623L19.6406 16.8799C19.7052 16.9232 19.7346 16.9996 19.7207 17.0713L19.6924 17.1396C19.1565 17.94 18.4968 18.6497 17.7441 19.2471L17.5352 18.9346C18.1339 18.456 18.6714 17.9039 19.1289 17.2881L19.3799 16.9316H19.3809C19.4311 16.8566 19.5264 16.8291 19.6074 16.8623ZM12 7.125C14.6924 7.125 16.8749 9.30763 16.875 12C16.875 14.6924 14.6924 16.875 12 16.875C9.30763 16.8749 7.125 14.6924 7.125 12C7.12508 9.30768 9.30768 7.12508 12 7.125ZM3.14648 10.0469C3.23507 10.0645 3.29728 10.1429 3.29785 10.2305L3.29395 10.2686C3.18327 10.8282 3.12503 11.4073 3.125 12L3.13086 12.332C3.14306 12.6626 3.17342 12.9885 3.2207 13.3086L3.22266 13.3174L3.22363 13.3262L3.28711 13.6934V13.6943L3.2959 13.7578C3.30033 13.8262 3.26726 13.8907 3.21094 13.9268L3.14648 13.9531C3.05242 13.9717 2.96029 13.9159 2.93164 13.8281L2.9248 13.7998L2.84863 13.3574C2.78348 12.9144 2.75001 12.4608 2.75 12L2.76172 11.54C2.7767 11.2349 2.80575 10.9335 2.84961 10.6367L2.92578 10.1953C2.94092 10.1189 3.00077 10.0627 3.07227 10.0479L3.14648 10.0469ZM21.1865 10.9365C21.2265 11.2857 21.25 11.6404 21.25 12L21.2383 12.4609C21.2161 12.9128 21.162 13.3563 21.0771 13.7891L21.0674 13.8281C21.0459 13.8936 20.9901 13.941 20.9238 13.9531H20.8535C20.7518 13.9328 20.6861 13.8336 20.7061 13.7324L20.708 13.7246L20.709 13.7158L20.7764 13.3281L20.7803 13.3086C20.8434 12.8809 20.875 12.4435 20.875 12L20.8643 11.5586C20.8552 11.374 20.8375 11.1912 20.8174 11.0098L21.1865 10.9365ZM6.88086 4.2959C6.94273 4.26456 7.01679 4.27193 7.07227 4.31055L7.12109 4.36035C7.17773 4.44575 7.15476 4.56129 7.06836 4.61914L7.05273 4.63086L6.72656 4.86133L6.71289 4.87109C5.99852 5.40185 5.3658 6.03774 4.83887 6.75586L4.63672 7.04395L4.60254 7.08984C4.54208 7.15747 4.43818 7.1738 4.35938 7.12109H4.36035C4.29528 7.07753 4.26556 7.00042 4.2793 6.92871L4.30762 6.86035L4.58105 6.47559L4.58008 6.47461C5.2073 5.63366 5.97424 4.90321 6.8457 4.31738L6.88086 4.2959ZM17.1396 4.30762C18.0169 4.89492 18.7883 5.62923 19.4189 6.47461V6.47559L19.6836 6.84863L19.7041 6.88184C19.7351 6.94366 19.7288 7.01698 19.6904 7.07227L19.6406 7.12109C19.5609 7.17429 19.4538 7.15783 19.3936 7.08594L19.3594 7.03906L19.1387 6.72656L19.1289 6.71289L18.8945 6.41211C18.4143 5.82018 17.8598 5.2906 17.2441 4.83887L16.9316 4.61914C16.8672 4.57602 16.8377 4.50044 16.8516 4.42871L16.8799 4.36035C16.9234 4.29533 16.9998 4.26657 17.0713 4.28027L17.1396 4.30762ZM12 2.75C12.4608 2.75001 12.9144 2.78348 13.3574 2.84863L13.7998 2.9248L13.8281 2.93164C13.8936 2.95302 13.941 3.0097 13.9531 3.07617V3.14648C13.9347 3.23945 13.8495 3.30184 13.7578 3.2959L13.6943 3.28711H13.6934L13.3262 3.22363L13.3174 3.22266L13.3086 3.2207L12.9863 3.17969C12.7705 3.1558 12.5524 3.13899 12.332 3.13086L12 3.125C11.5554 3.12502 11.1183 3.15759 10.6914 3.2207L10.2686 3.29395C10.1921 3.30906 10.1173 3.27531 10.0762 3.21484L10.0469 3.14648C10.0294 3.05808 10.0779 2.97145 10.1582 2.9375L10.1953 2.92578C10.6332 2.83917 11.0824 2.78418 11.54 2.76172L12 2.75Z" fill="currentColor" stroke="currentColor" stroke-width="1.5"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
@@ -0,0 +1,16 @@
|
||||
import { PriorityLevel } from "./PriorityLevel.tsx";
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
|
||||
export default {
|
||||
title: "Atoms/PriorityLevel",
|
||||
component: PriorityLevel,
|
||||
argTypes: {},
|
||||
} satisfies Meta<typeof PriorityLevel>;
|
||||
|
||||
type Story = StoryObj<typeof PriorityLevel>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
level: 1,
|
||||
},
|
||||
};
|
||||
30
packages/ui/src/Atoms/PriorityLevel/PriorityLevel.tsx
Normal file
30
packages/ui/src/Atoms/PriorityLevel/PriorityLevel.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import clsx from "clsx";
|
||||
|
||||
type Props = {
|
||||
level: number;
|
||||
};
|
||||
|
||||
export function PriorityLevel({ level }: Props) {
|
||||
return (
|
||||
<div className="w-max p-[2px] flex gap-[2px] items-end">
|
||||
<div
|
||||
className={clsx(
|
||||
"h-1 w-[3px] bg-txt-quaternary rounded",
|
||||
level >= 1 ? "bg-txt-secondary" : "bg-txt-quaternary",
|
||||
)}
|
||||
/>
|
||||
<div
|
||||
className={clsx(
|
||||
"h-2 w-[3px] bg-txt-quaternary rounded",
|
||||
level >= 2 ? "bg-txt-secondary" : "bg-txt-quaternary",
|
||||
)}
|
||||
/>
|
||||
<div
|
||||
className={clsx(
|
||||
"h-3 w-[3px] bg-txt-quaternary rounded",
|
||||
level >= 3 ? "bg-txt-secondary" : "bg-txt-quaternary",
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -13,14 +13,14 @@ export function PropertyRow({ id, label, children, error, ...props }: Props) {
|
||||
|
||||
return (
|
||||
<div className="py-3 border-b border-border-low space-y-2" {...props}>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<Label
|
||||
className="text-sm text-txt-secondary font-medium mb-0"
|
||||
htmlFor={id}
|
||||
>
|
||||
{label}
|
||||
</Label>
|
||||
<div>{firstChild}</div>
|
||||
<div className="min-w-0">{firstChild}</div>
|
||||
</div>
|
||||
{error && <div className="text-xs text-txt-danger">{error}</div>}
|
||||
{restChildren}
|
||||
|
||||
@@ -10,10 +10,11 @@ import {
|
||||
ItemText,
|
||||
} from "@radix-ui/react-select";
|
||||
import * as ScrollArea from "@radix-ui/react-scroll-area";
|
||||
import { input } from "../Input/Input.tsx";
|
||||
import { Input, input } from "../Input/Input.tsx";
|
||||
import { IconChevronGrabberVertical } from "../Icons/IconChevronGrabberVertical.tsx";
|
||||
import { tv } from "tailwind-variants";
|
||||
import { Children, type ComponentProps, type PropsWithChildren } from "react";
|
||||
import { IconMagnifyingGlass } from "../Icons/IconMagnifyingGlass.tsx";
|
||||
|
||||
type Props<T> = PropsWithChildren<
|
||||
{
|
||||
@@ -25,6 +26,9 @@ type Props<T> = PropsWithChildren<
|
||||
disabled?: boolean;
|
||||
className?: string;
|
||||
value?: T;
|
||||
searchValue?: string;
|
||||
onSearch?: (s: string) => void;
|
||||
searchPlaceholder?: string;
|
||||
} & Omit<ComponentProps<typeof Root>, "onChange" | "value">
|
||||
>;
|
||||
|
||||
@@ -42,6 +46,11 @@ const select = tv({
|
||||
trigger: "border-border-danger",
|
||||
},
|
||||
},
|
||||
disabled: {
|
||||
true: {
|
||||
trigger: "opacity-60",
|
||||
},
|
||||
},
|
||||
variant: {
|
||||
dashed: {
|
||||
trigger: input({
|
||||
@@ -76,6 +85,9 @@ export function Select<T>({
|
||||
children,
|
||||
onValueChange,
|
||||
value,
|
||||
searchValue,
|
||||
onSearch,
|
||||
searchPlaceholder,
|
||||
...props
|
||||
}: Props<T>) {
|
||||
const { trigger, content, icon } = select({
|
||||
@@ -86,7 +98,10 @@ export function Select<T>({
|
||||
<Root onValueChange={onValueChange} value={value as string}>
|
||||
<Trigger
|
||||
{...props}
|
||||
className={trigger({ className: props.className })}
|
||||
className={trigger({
|
||||
className: props.className,
|
||||
disabled: props.disabled,
|
||||
})}
|
||||
>
|
||||
<Value placeholder={placeholder} />
|
||||
<Icon className={icon()}>
|
||||
@@ -104,6 +119,20 @@ export function Select<T>({
|
||||
"var(--radix-select-content-available-height)",
|
||||
}}
|
||||
>
|
||||
{onSearch && (
|
||||
<Input
|
||||
// Prevent radix behaviour
|
||||
onBlurCapture={(e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}}
|
||||
autoFocus
|
||||
icon={IconMagnifyingGlass}
|
||||
placeholder={searchPlaceholder}
|
||||
value={searchValue}
|
||||
onChange={(e) => onSearch(e.target.value)}
|
||||
/>
|
||||
)}
|
||||
<ScrollArea.Root className="ScrollAreaRoot" type="auto">
|
||||
<Viewport asChild>
|
||||
<ScrollArea.Viewport className="ScrollAreaViewport">
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { ReactNode } from "react";
|
||||
|
||||
export type AsChildProps<DefaultElementProps> =
|
||||
| ({ asChild?: false } & DefaultElementProps)
|
||||
| { asChild: true; children: ReactNode };
|
||||
| ({ asChild: true; children: ReactNode } & DefaultElementProps);
|
||||
|
||||
export function Slot({
|
||||
children,
|
||||
|
||||
@@ -1,41 +1,55 @@
|
||||
import type { PropsWithChildren } from "react";
|
||||
import type { HTMLAttributes, PropsWithChildren } from "react";
|
||||
import { NavLink } from "react-router";
|
||||
import { Root, List } from "@radix-ui/react-tabs";
|
||||
import clsx from "clsx";
|
||||
import { Slot, type AsChildProps } from "../Slot";
|
||||
import { tv } from "tailwind-variants";
|
||||
|
||||
export function Tabs(props: PropsWithChildren) {
|
||||
const cls = tv({
|
||||
slots: {
|
||||
wrapper:
|
||||
"border-b border-border-low flex gap-6 text-sm font-medium text-txt-secondary",
|
||||
item: "py-4 hover:text-txt-primary border-b-2 active:border-border-active -mb-[1px] active:text-txt-primary flex items-center gap-2",
|
||||
badge: "py-1 px-2 text-txt-secondary text-xs font-semibold rounded-lg bg-highlight",
|
||||
},
|
||||
variants: {
|
||||
active: {
|
||||
true: {
|
||||
item: "border-border-active text-txt-primary",
|
||||
wrapper: "border-border-active",
|
||||
},
|
||||
false: {
|
||||
item: "border-transparent",
|
||||
},
|
||||
},
|
||||
},
|
||||
})();
|
||||
|
||||
export function Tabs(props: HTMLAttributes<HTMLElement>) {
|
||||
return (
|
||||
<Root className="TabsRoot" defaultValue="tab1">
|
||||
<List className="TabsList" aria-label="Manage your account">
|
||||
<div
|
||||
className="border-b border-border-low flex gap-6 text-sm font-medium text-txt-secondary"
|
||||
{...props}
|
||||
/>
|
||||
</List>
|
||||
<Root>
|
||||
<List {...props} className={cls.wrapper(props)} />
|
||||
</Root>
|
||||
);
|
||||
}
|
||||
|
||||
export function TabItem({
|
||||
asChild,
|
||||
active,
|
||||
...props
|
||||
}: AsChildProps<{ active?: boolean }>) {
|
||||
const Component = asChild ? Slot : "div";
|
||||
return <Component {...props} className={cls.item({ active })} />;
|
||||
}
|
||||
|
||||
export function TabLink(props: PropsWithChildren<{ to: string }>) {
|
||||
return (
|
||||
<NavLink
|
||||
className={(params) =>
|
||||
clsx(
|
||||
"py-4 hover:text-txt-primary border-b-2 active:border-border-active -mb-[1px] active:text-txt-primary flex items-center gap-1",
|
||||
params.isActive
|
||||
? "border-border-active text-txt-primary"
|
||||
: "border-transparent",
|
||||
)
|
||||
}
|
||||
className={(params) => cls.item({ active: params.isActive })}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function TabBadge(props: PropsWithChildren) {
|
||||
return (
|
||||
<span className="py-1 px-2 text-txt-secondary text-xs font-semibold rounded-lg bg-highlight text-primary">
|
||||
{props.children}
|
||||
</span>
|
||||
);
|
||||
return <span className={cls.badge()}>{props.children}</span>;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ type Props = {
|
||||
onSelect?: (value: string) => void;
|
||||
resetValueOnHide?: boolean;
|
||||
value?: string;
|
||||
};
|
||||
} & Omit<ComponentProps<typeof AriaKitCombobox>, "onSelect" | "value">;
|
||||
|
||||
export function Combobox({
|
||||
children,
|
||||
@@ -32,6 +32,7 @@ export function Combobox({
|
||||
onSelect,
|
||||
resetValueOnHide,
|
||||
value,
|
||||
...props
|
||||
}: Props) {
|
||||
const showDropdown = !isEmpty(children);
|
||||
return (
|
||||
@@ -42,6 +43,7 @@ export function Combobox({
|
||||
resetValueOnHide={resetValueOnHide}
|
||||
>
|
||||
<AriaKitCombobox
|
||||
{...props}
|
||||
autoSelect={autoSelect}
|
||||
placeholder={placeholder}
|
||||
className={input()}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { useState } from "react";
|
||||
import { DurationPicker } from "./DurationPicker";
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
|
||||
export default {
|
||||
title: "Molecules/DurationPicker",
|
||||
component: DurationPicker,
|
||||
argTypes: {},
|
||||
} satisfies Meta<typeof DurationPicker>;
|
||||
|
||||
type Story = StoryObj<typeof DurationPicker>;
|
||||
|
||||
export const Default: Story = {
|
||||
render() {
|
||||
const [value, setValue] = useState<string | null>("PT1H");
|
||||
return <DurationPicker value={value} onValueChange={setValue} />;
|
||||
},
|
||||
};
|
||||
83
packages/ui/src/Molecules/DurationPicker/DurationPicker.tsx
Normal file
83
packages/ui/src/Molecules/DurationPicker/DurationPicker.tsx
Normal file
@@ -0,0 +1,83 @@
|
||||
import type { HTMLAttributes } from "react";
|
||||
import { Button } from "../../Atoms/Button/Button";
|
||||
import { IconPlusLarge } from "../../Atoms/Icons";
|
||||
import { Input } from "../../Atoms/Input/Input";
|
||||
import { Option, Select } from "../../Atoms/Select/Select";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
|
||||
type Props = {
|
||||
value: string | null;
|
||||
onValueChange: (value: string | null) => void;
|
||||
} & HTMLAttributes<HTMLInputElement>;
|
||||
|
||||
const stringify = (value: number | null, unit: string): string | null => {
|
||||
if (value === null || value <= 0) return null;
|
||||
|
||||
switch (unit) {
|
||||
case "M":
|
||||
return `PT${value}M`;
|
||||
case "H":
|
||||
return `PT${value}H`;
|
||||
case "D":
|
||||
return `P${value}D`;
|
||||
case "W":
|
||||
return `P${value * 7}D`;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const parse = (value: string): { amount: number; unit: string } | null => {
|
||||
const match = value.match(/PT?(\d+)([MDWH])/);
|
||||
if (!match) return { amount: 0, unit: "D" };
|
||||
const amount = parseInt(match[1], 10) || 0;
|
||||
const unit = match[2];
|
||||
if (amount % 7 === 0 && unit === "D") {
|
||||
return { amount: amount / 7, unit: "W" };
|
||||
}
|
||||
return { amount, unit };
|
||||
};
|
||||
|
||||
export function DurationPicker({ value, onValueChange, ...props }: Props) {
|
||||
const { __ } = useTranslate();
|
||||
if (!value) {
|
||||
return (
|
||||
<div>
|
||||
<Button
|
||||
variant="secondary"
|
||||
icon={IconPlusLarge}
|
||||
onClick={() => onValueChange("PT1H")}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const { amount, unit } = parse(value || "") || { amount: 0, unit: "D" };
|
||||
|
||||
return (
|
||||
<div className="flex gap-2 w-max">
|
||||
<Input
|
||||
{...props}
|
||||
className="w-25 flex-none"
|
||||
type="number"
|
||||
step={1}
|
||||
value={amount}
|
||||
onChange={(e) =>
|
||||
onValueChange(stringify(e.target.valueAsNumber, unit))
|
||||
}
|
||||
/>
|
||||
<Select
|
||||
className="w-max flex-none"
|
||||
value={unit}
|
||||
onValueChange={(v: string) =>
|
||||
onValueChange(stringify(amount, v))
|
||||
}
|
||||
>
|
||||
<Option value="M">{__("Minutes")}</Option>
|
||||
<Option value="H">{__("Hours")}</Option>
|
||||
<Option value="D">{__("Days")}</Option>
|
||||
<Option value="W">{__("Weeks")}</Option>
|
||||
</Select>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -37,7 +37,7 @@ export function MeasureImplementation({ measures, className }: Props) {
|
||||
<h2 className="text-base font-medium">
|
||||
{__("Measure implementation")}
|
||||
</h2>
|
||||
<div className="h-2 rounded bg-highlight flex justify-stretch item-stretch">
|
||||
<div className="h-2 rounded overflow-hidden bg-highlight flex justify-stretch item-stretch">
|
||||
{measureStates.map((state) => (
|
||||
<div
|
||||
key={state}
|
||||
@@ -45,7 +45,7 @@ export function MeasureImplementation({ measures, className }: Props) {
|
||||
style={{
|
||||
flexGrow: counts[state] ?? 0,
|
||||
}}
|
||||
></div>
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex gap-4 text-sm">
|
||||
|
||||
@@ -30,11 +30,13 @@ export { Select, Option } from "./Atoms/Select/Select.tsx";
|
||||
export { Label } from "./Atoms/Label/Label";
|
||||
export { PropertyRow } from "./Atoms/PropertyRow/PropertyRow";
|
||||
export { Table, Thead, Tr, Tbody, Td, Th, TrButton } from "./Atoms/Table/Table";
|
||||
export { Tabs, TabLink, TabBadge } from "./Atoms/Tabs/Tabs";
|
||||
export { Tabs, TabLink, TabBadge, TabItem } from "./Atoms/Tabs/Tabs";
|
||||
export { Markdown } from "./Atoms/Markdown/Markdown";
|
||||
export { Dropzone } from "./Atoms/Dropzone/Dropzone";
|
||||
export { ControlItem } from "./Atoms/ControlItem/ControlItem";
|
||||
export { InfiniteScrollTrigger } from "./Atoms/InfiniteScrollTrigger/InfiniteScrollTrigger";
|
||||
export { PriorityLevel } from "./Atoms/PriorityLevel/PriorityLevel.tsx";
|
||||
export { TaskStateIcon } from "./Atoms/Icons/TaskStateIcon";
|
||||
|
||||
// Molecules
|
||||
export {
|
||||
@@ -65,6 +67,7 @@ export { FrameworkSelector } from "./Molecules/FrameworkSelector/FrameworkSelect
|
||||
export { DocumentTypeBadge } from "./Molecules/Badge/DocumentTypeBadge";
|
||||
export { SentitivityOptions } from "./Molecules/Select/SentitivityOptions";
|
||||
export { ImpactOptions } from "./Molecules/Select/ImpactOptions";
|
||||
export { DurationPicker } from "./Molecules/DurationPicker/DurationPicker";
|
||||
|
||||
// Hooks
|
||||
export { useToast, Toasts } from "./Atoms/Toasts/Toasts";
|
||||
|
||||
Reference in New Issue
Block a user