Add policies signature history

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
Jonathan
2025-05-27 12:32:04 +02:00
committed by Sacha Al Himdani
parent 2e6741942b
commit 61b1a24149
45 changed files with 1154 additions and 946 deletions

View File

@@ -5,7 +5,7 @@
"type": "module",
"scripts": {
"dev": "vite",
"relay": "find src -type d -name \"__generated\" -exec rm -rf {} + && npx relay-compiler",
"relay": "find src -type d -name \"__generated__\" -exec rm -rf {} + && npx relay-compiler ./relay.config.json",
"build": "tsc -b && vite build",
"lint": "eslint .",
"check": "tsc --noEmit -p tsconfig.app.json",

View File

@@ -2,5 +2,6 @@
"src": "./src",
"schema": "../../pkg/server/api/console/v1/schema.graphql",
"language": "typescript",
"eagerEsModules": true
"eagerEsModules": true,
"noFutureProofEnums": true
}

View File

@@ -1,27 +1,8 @@
import { Avatar, Option, Select } from "@probo/ui";
import { Suspense } from "react";
import { useLazyLoadQuery } from "react-relay";
import { graphql } from "relay-runtime";
import type { PeopleSelectQuery as PeopleSelectQueryType } from "./__generated__/PeopleSelectQuery.graphql";
import { useTranslate } from "@probo/i18n";
import { Controller, type Control } from "react-hook-form";
const peopleQuery = graphql`
query PeopleSelectQuery($organizationId: ID!) {
organization: node(id: $organizationId) {
... on Organization {
peoples(first: 100, orderBy: { direction: ASC, field: CREATED_AT }) {
edges {
node {
id
fullName
}
}
}
}
}
}
`;
import { type Control, Controller } from "react-hook-form";
import { usePeople } from "/hooks/graph/PeopleGraph.ts";
export function PeopleSelect({
organization,
@@ -55,11 +36,7 @@ function PeopleSelectWithQuery({
control: Control;
}) {
const { __ } = useTranslate();
const data = useLazyLoadQuery<PeopleSelectQueryType>(peopleQuery, {
organizationId: organization,
});
const people = data.organization?.peoples?.edges.map((edge) => edge.node);
const people = usePeople(organization);
return (
<>

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<391c4db090caa19c60582c18d426764a>>
* @generated SignedSource<<043e8cc5beda1ac8949a7c02adcff4bf>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -9,7 +9,7 @@
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type MeasureState = "IMPLEMENTED" | "IN_PROGRESS" | "NOT_APPLICABLE" | "NOT_STARTED" | "%future added value";
export type MeasureState = "IMPLEMENTED" | "IN_PROGRESS" | "NOT_APPLICABLE" | "NOT_STARTED";
export type CreateRiskMeasureMappingInput = {
measureId: string;
riskId: string;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<746421c9f8fc0c80ed1fd5643b3bbae8>>
* @generated SignedSource<<6a9b2c4428ed2255dda81771894c0c6c>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -9,7 +9,7 @@
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type MeasureState = "IMPLEMENTED" | "IN_PROGRESS" | "NOT_APPLICABLE" | "NOT_STARTED" | "%future added value";
export type MeasureState = "IMPLEMENTED" | "IN_PROGRESS" | "NOT_APPLICABLE" | "NOT_STARTED";
export type MeasureLinkDialogQuery$variables = {
organizationId: string;
};

View File

@@ -1,76 +0,0 @@
/**
* @generated SignedSource<<3ffcc7618d81b47e39ca5731fe75e2ed>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ReaderFragment } from 'relay-runtime';
import { FragmentRefs } from "relay-runtime";
export type usePolicyFormFragment$data = {
readonly description: string;
readonly id: string;
readonly owner: {
readonly id: string;
};
readonly title: string;
readonly " $fragmentType": "usePolicyFormFragment";
};
export type usePolicyFormFragment$key = {
readonly " $data"?: usePolicyFormFragment$data;
readonly " $fragmentSpreads": FragmentRefs<"usePolicyFormFragment">;
};
const node: ReaderFragment = (function(){
var v0 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "id",
"storageKey": null
};
return {
"argumentDefinitions": [],
"kind": "Fragment",
"metadata": null,
"name": "usePolicyFormFragment",
"selections": [
(v0/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "title",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "description",
"storageKey": null
},
{
"alias": null,
"args": null,
"concreteType": "People",
"kind": "LinkedField",
"name": "owner",
"plural": false,
"selections": [
(v0/*: any*/)
],
"storageKey": null
}
],
"type": "Policy",
"abstractKey": null
};
})();
(node as any).hash = "619d6845e45fb8c052a8989ea546414d";
export default node;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<6cf0470d3c2e5d8d9d6ca52bf3375026>>
* @generated SignedSource<<38b7fae585eb2c09409935db8ad3cfe9>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -9,7 +9,7 @@
// @ts-nocheck
import { ReaderFragment } from 'relay-runtime';
export type RiskTreatment = "ACCEPTED" | "AVOIDED" | "MITIGATED" | "TRANSFERRED" | "%future added value";
export type RiskTreatment = "ACCEPTED" | "AVOIDED" | "MITIGATED" | "TRANSFERRED";
import { FragmentRefs } from "relay-runtime";
export type useRiskFormFragment$data = {
readonly category: string;

View File

@@ -0,0 +1,31 @@
import { graphql } from "relay-runtime";
import type { PeopleGraphQuery } from "./__generated__/PeopleGraphQuery.graphql";
import { useLazyLoadQuery } from "react-relay";
import { useMemo } from "react";
const peopleQuery = graphql`
query PeopleGraphQuery($organizationId: ID!) {
organization: node(id: $organizationId) {
... on Organization {
peoples(first: 100, orderBy: { direction: ASC, field: CREATED_AT }) {
edges {
node {
id
fullName
primaryEmailAddress
}
}
}
}
}
}
`;
export function usePeople(organizationId: string) {
const data = useLazyLoadQuery<PeopleGraphQuery>(peopleQuery, {
organizationId: organizationId,
});
return useMemo(() => {
return data.organization?.peoples?.edges.map((edge) => edge.node) ?? [];
}, [data]);
}

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<758e3ece2d7d1d52975886d41b6ae905>>
* @generated SignedSource<<fbc31c6da75cbb26f402b2c88664950a>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -9,24 +9,25 @@
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type PeopleSelectQuery$variables = {
export type PeopleGraphQuery$variables = {
organizationId: string;
};
export type PeopleSelectQuery$data = {
export type PeopleGraphQuery$data = {
readonly organization: {
readonly peoples?: {
readonly edges: ReadonlyArray<{
readonly node: {
readonly fullName: string;
readonly id: string;
readonly primaryEmailAddress: string;
};
}>;
};
};
};
export type PeopleSelectQuery = {
response: PeopleSelectQuery$data;
variables: PeopleSelectQuery$variables;
export type PeopleGraphQuery = {
response: PeopleGraphQuery$data;
variables: PeopleGraphQuery$variables;
};
const node: ConcreteRequest = (function(){
@@ -99,6 +100,13 @@ v3 = {
"kind": "ScalarField",
"name": "fullName",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "primaryEmailAddress",
"storageKey": null
}
],
"storageKey": null
@@ -118,7 +126,7 @@ return {
"argumentDefinitions": (v0/*: any*/),
"kind": "Fragment",
"metadata": null,
"name": "PeopleSelectQuery",
"name": "PeopleGraphQuery",
"selections": [
{
"alias": "organization",
@@ -140,7 +148,7 @@ return {
"operation": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Operation",
"name": "PeopleSelectQuery",
"name": "PeopleGraphQuery",
"selections": [
{
"alias": "organization",
@@ -165,16 +173,16 @@ return {
]
},
"params": {
"cacheID": "4e58fc42b352af93ba7067921531af3e",
"cacheID": "cf76244f6aaa0c573bdeb7bf118ad2a3",
"id": null,
"metadata": {},
"name": "PeopleSelectQuery",
"name": "PeopleGraphQuery",
"operationKind": "query",
"text": "query PeopleSelectQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n ... on Organization {\n peoples(first: 100, orderBy: {direction: ASC, field: CREATED_AT}) {\n edges {\n node {\n id\n fullName\n }\n }\n }\n }\n id\n }\n}\n"
"text": "query PeopleGraphQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n ... on Organization {\n peoples(first: 100, orderBy: {direction: ASC, field: CREATED_AT}) {\n edges {\n node {\n id\n fullName\n primaryEmailAddress\n }\n }\n }\n }\n id\n }\n}\n"
}
};
})();
(node as any).hash = "77119544e99cf9dd74d8a0f446df108e";
(node as any).hash = "11220f45320400c451abeaf7479a8e47";
export default node;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<26cd4feab2589e8b641b339c977905a3>>
* @generated SignedSource<<b9027c9c44291af1bebb38760fd96fc8>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -53,20 +53,11 @@ v3 = {
"name": "__typename",
"storageKey": null
},
v4 = {
"kind": "Literal",
"name": "first",
"value": 100
},
v5 = [
(v4/*: any*/),
v4 = [
{
"kind": "Literal",
"name": "orderBy",
"value": {
"direction": "DESC",
"field": "CREATED_AT"
}
"name": "first",
"value": 100
}
];
return {
@@ -118,7 +109,7 @@ return {
"selections": [
{
"alias": null,
"args": (v5/*: any*/),
"args": (v4/*: any*/),
"concreteType": "PolicyConnection",
"kind": "LinkedField",
"name": "policies",
@@ -221,9 +212,7 @@ return {
},
{
"alias": null,
"args": [
(v4/*: any*/)
],
"args": (v4/*: any*/),
"concreteType": "PolicyVersionSignatureConnection",
"kind": "LinkedField",
"name": "signatures",
@@ -306,20 +295,6 @@ return {
"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
@@ -337,14 +312,12 @@ return {
]
}
],
"storageKey": "policies(first:100,orderBy:{\"direction\":\"DESC\",\"field\":\"CREATED_AT\"})"
"storageKey": "policies(first:100)"
},
{
"alias": null,
"args": (v5/*: any*/),
"filters": [
"orderBy"
],
"args": (v4/*: any*/),
"filters": null,
"handle": "connection",
"key": "PoliciesPageFragment_policies",
"kind": "LinkedHandle",
@@ -360,12 +333,12 @@ return {
]
},
"params": {
"cacheID": "838223ae76604de3893662f5a496c1d3",
"cacheID": "b0b3546b418ff2cfd5dd7678d3b0be14",
"id": null,
"metadata": {},
"name": "PolicyGraphListQuery",
"operationKind": "query",
"text": "query PolicyGraphListQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n id\n ...PoliciesPageListFragment\n }\n}\n\nfragment PoliciesPageListFragment on Organization {\n policies(first: 100, orderBy: {direction: DESC, field: CREATED_AT}) {\n edges {\n node {\n id\n ...PoliciesPageRowFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n}\n\nfragment PoliciesPageRowFragment on Policy {\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": "query PolicyGraphListQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n id\n ...PoliciesPageListFragment\n }\n}\n\nfragment PoliciesPageListFragment on Organization {\n policies(first: 100) {\n edges {\n node {\n id\n ...PoliciesPageRowFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n\nfragment PoliciesPageRowFragment on Policy {\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"
}
};
})();

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<bfeead50dec41ac2f5fac7c6d70f47fb>>
* @generated SignedSource<<b7defae939287f936c1eb687fe2d9bfe>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -65,7 +65,58 @@ v5 = [
"name": "first",
"value": 20
}
];
],
v6 = [
{
"kind": "Literal",
"name": "first",
"value": 100
}
],
v7 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "cursor",
"storageKey": null
},
v8 = {
"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
},
v9 = {
"kind": "ClientExtension",
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "__id",
"storageKey": null
}
]
};
return {
"fragment": {
"argumentDefinitions": (v0/*: any*/),
@@ -201,13 +252,7 @@ return {
},
{
"alias": null,
"args": [
{
"kind": "Literal",
"name": "first",
"value": 100
}
],
"args": (v6/*: any*/),
"concreteType": "PolicyVersionSignatureConnection",
"kind": "LinkedField",
"name": "signatures",
@@ -236,16 +281,63 @@ return {
"kind": "ScalarField",
"name": "state",
"storageKey": null
}
},
{
"alias": null,
"args": null,
"concreteType": "People",
"kind": "LinkedField",
"name": "signedBy",
"plural": false,
"selections": [
(v3/*: any*/),
(v4/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "primaryEmailAddress",
"storageKey": null
}
],
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "signedAt",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "requestedAt",
"storageKey": null
},
(v2/*: any*/)
],
"storageKey": null
}
},
(v7/*: any*/)
],
"storageKey": null
}
},
(v8/*: any*/),
(v9/*: any*/)
],
"storageKey": "signatures(first:100)"
},
{
"alias": null,
"args": (v6/*: any*/),
"filters": null,
"handle": "connection",
"key": "PolicyPage_signatures",
"kind": "LinkedHandle",
"name": "signatures"
},
{
"alias": null,
"args": null,
@@ -270,53 +362,12 @@ return {
],
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "cursor",
"storageKey": null
}
(v7/*: any*/)
],
"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
}
]
}
(v8/*: any*/),
(v9/*: any*/)
],
"storageKey": "versions(first:20)"
},
@@ -339,12 +390,12 @@ return {
]
},
"params": {
"cacheID": "e3a62b161b9fce47237c98f90a5d6d2e",
"cacheID": "e9c0e78ebbf62684edbfa11ac1ee5d25",
"id": null,
"metadata": {},
"name": "PolicyGraphNodeQuery",
"operationKind": "query",
"text": "query PolicyGraphNodeQuery(\n $policyId: ID!\n) {\n node(id: $policyId) {\n __typename\n ... on Policy {\n ...PolicyPagePolicyFragment\n }\n id\n }\n}\n\nfragment PolicyPagePolicyFragment on Policy {\n id\n title\n owner {\n id\n fullName\n }\n versions(first: 20) {\n edges {\n node {\n id\n content\n status\n publishedAt\n version\n updatedAt\n signatures(first: 100) {\n edges {\n node {\n id\n state\n }\n }\n }\n ...PolicyVersionHistoryDialogFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n\nfragment PolicyVersionHistoryDialogFragment on PolicyVersion {\n id\n version\n status\n content\n changelog\n publishedAt\n updatedAt\n publishedBy {\n fullName\n id\n }\n}\n"
"text": "query PolicyGraphNodeQuery(\n $policyId: ID!\n) {\n node(id: $policyId) {\n __typename\n ... on Policy {\n ...PolicyPagePolicyFragment\n }\n id\n }\n}\n\nfragment PolicyPagePolicyFragment on Policy {\n id\n title\n owner {\n id\n fullName\n }\n versions(first: 20) {\n edges {\n node {\n id\n content\n status\n publishedAt\n version\n updatedAt\n signatures(first: 100) {\n edges {\n node {\n id\n state\n signedBy {\n id\n }\n ...PolicySignaturesDialog_signature\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n ...PolicyVersionHistoryDialogFragment\n ...PolicySignaturesDialog_version\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n\nfragment PolicySignaturesDialog_signature on PolicyVersionSignature {\n id\n state\n signedAt\n requestedAt\n signedBy {\n fullName\n primaryEmailAddress\n id\n }\n}\n\nfragment PolicySignaturesDialog_version on PolicyVersion {\n version\n status\n publishedAt\n updatedAt\n}\n\nfragment PolicyVersionHistoryDialogFragment on PolicyVersion {\n id\n version\n status\n content\n changelog\n publishedAt\n updatedAt\n publishedBy {\n fullName\n id\n }\n}\n"
}
};
})();

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<352ca1fa13e246dd48eb0cd346a251ef>>
* @generated SignedSource<<d65dc38bcf11079a304f8a5cc4070389>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -9,7 +9,7 @@
// @ts-nocheck
import { ReaderFragment } from 'relay-runtime';
export type RiskTreatment = "ACCEPTED" | "AVOIDED" | "MITIGATED" | "TRANSFERRED" | "%future added value";
export type RiskTreatment = "ACCEPTED" | "AVOIDED" | "MITIGATED" | "TRANSFERRED";
import { FragmentRefs } from "relay-runtime";
export type RiskGraphFragment$data = {
readonly id: string;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<3485aabb6e5e1992e2f0c089deaf4b2d>>
* @generated SignedSource<<18341e1d2a7f480ad73ef32506121c5e>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -10,7 +10,7 @@
import { ConcreteRequest } from 'relay-runtime';
import { FragmentRefs } from "relay-runtime";
export type RiskTreatment = "ACCEPTED" | "AVOIDED" | "MITIGATED" | "TRANSFERRED" | "%future added value";
export type RiskTreatment = "ACCEPTED" | "AVOIDED" | "MITIGATED" | "TRANSFERRED";
export type RiskGraphNodeQuery$variables = {
riskId: string;
};

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<5cce23056dd540654c65719414023e64>>
* @generated SignedSource<<7c5151b921734cd14b58af145eba7267>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -10,8 +10,8 @@
import { ConcreteRequest } from 'relay-runtime';
import { FragmentRefs } from "relay-runtime";
export type OrderDirection = "ASC" | "DESC" | "%future added value";
export type RiskOrderField = "CATEGORY" | "CREATED_AT" | "INITIAL_RISK_SCORE" | "NAME" | "RESIDUAL_RISK_SCORE" | "TREATMENT" | "UPDATED_AT" | "%future added value";
export type OrderDirection = "ASC" | "DESC";
export type RiskOrderField = "CATEGORY" | "CREATED_AT" | "INITIAL_RISK_SCORE" | "NAME" | "RESIDUAL_RISK_SCORE" | "TREATMENT" | "UPDATED_AT";
export type RiskOrder = {
direction: OrderDirection;
field: RiskOrderField;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<a35e945926fc7614549d452b1edd92f9>>
* @generated SignedSource<<d474691920e943cf5df04fa93c746b28>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -9,7 +9,7 @@
// @ts-nocheck
import { ReaderFragment } from 'relay-runtime';
export type MeasureState = "IMPLEMENTED" | "IN_PROGRESS" | "NOT_APPLICABLE" | "NOT_STARTED" | "%future added value";
export type MeasureState = "IMPLEMENTED" | "IN_PROGRESS" | "NOT_APPLICABLE" | "NOT_STARTED";
import { FragmentRefs } from "relay-runtime";
export type MeasuresPageFragment$data = {
readonly measures: {

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<5a6e0e680ef636cf776a56f490733aee>>
* @generated SignedSource<<c0e424b8b0ec7becf9ac2930c6e81b56>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -9,7 +9,7 @@
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type MeasureState = "IMPLEMENTED" | "IN_PROGRESS" | "NOT_APPLICABLE" | "NOT_STARTED" | "%future added value";
export type MeasureState = "IMPLEMENTED" | "IN_PROGRESS" | "NOT_APPLICABLE" | "NOT_STARTED";
export type ImportMeasureInput = {
file: any;
organizationId: string;

View File

@@ -33,13 +33,7 @@ import type { PoliciesPageRowFragment$key } from "./__generated__/PoliciesPageRo
const policiesFragment = graphql`
fragment PoliciesPageListFragment on Organization {
policies(
first: 100
after: null
last: null
before: null
orderBy: { direction: DESC, field: CREATED_AT }
) @connection(key: "PoliciesPageFragment_policies") {
policies(first: 100) @connection(key: "PoliciesPageFragment_policies") {
__id
edges {
node {

View File

@@ -31,6 +31,7 @@ import {
IconPencil,
useConfirmDialogRef,
IconClock,
IconSignature,
} from "@probo/ui";
import { useOrganizationId } from "/hooks/useOrganizationId";
import { Button } from "@probo/ui";
@@ -40,6 +41,7 @@ import { useNavigate } from "react-router";
import UpdateVersionDialog from "./dialogs/UpdateVersionDialog";
import { useRef } from "react";
import { PolicyVersionHistoryDialog } from "./dialogs/PolicyVersionHistoryDialog";
import { PolicySignaturesDialog } from "./dialogs/PolicySignaturesDialog";
type Props = {
queryRef: PreloadedQuery<PolicyGraphNodeQuery>;
@@ -63,15 +65,21 @@ const policyFragment = graphql`
publishedAt
version
updatedAt
signatures(first: 100) {
signatures(first: 100) @connection(key: "PolicyPage_signatures") {
__id
edges {
node {
id
state
signedBy {
id
}
...PolicySignaturesDialog_signature
}
}
}
...PolicyVersionHistoryDialogFragment
...PolicySignaturesDialog_version
}
}
}
@@ -108,7 +116,6 @@ export default function PolicyPage(props: Props) {
);
const [deletePolicy, isDeleting] = useDeletePolicyMutation();
const versionConnectionId = policy.versions.__id;
const versions = policy.versions.edges.map((edge) => edge.node);
usePageTitle(policy.title);
@@ -197,6 +204,11 @@ export default function PolicyPage(props: Props) {
{__("Version history")}
</Button>
</PolicyVersionHistoryDialog>
<PolicySignaturesDialog policy={policy}>
<Button icon={IconSignature} variant="secondary">
{__("Signatures history")}
</Button>
</PolicySignaturesDialog>
<ActionDropdown variant="secondary">
<DropdownItem

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<cd6428905d88d31ab94cb3cbcd9fcd2c>>
* @generated SignedSource<<89063899241b0557e2a6df4ff74ecea1>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -35,7 +35,7 @@ const node: ReaderFragment = {
{
"count": null,
"cursor": null,
"direction": "bidirectional",
"direction": "forward",
"path": [
"policies"
]
@@ -46,16 +46,7 @@ const node: ReaderFragment = {
"selections": [
{
"alias": "policies",
"args": [
{
"kind": "Literal",
"name": "orderBy",
"value": {
"direction": "DESC",
"field": "CREATED_AT"
}
}
],
"args": null,
"concreteType": "PolicyConnection",
"kind": "LinkedField",
"name": "__PoliciesPageFragment_policies_connection",
@@ -130,20 +121,6 @@ const node: ReaderFragment = {
"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
@@ -161,13 +138,13 @@ const node: ReaderFragment = {
]
}
],
"storageKey": "__PoliciesPageFragment_policies_connection(orderBy:{\"direction\":\"DESC\",\"field\":\"CREATED_AT\"})"
"storageKey": null
}
],
"type": "Organization",
"abstractKey": null
};
(node as any).hash = "2f34601ba53a0900494a1bb0d8be1773";
(node as any).hash = "1c1fa3340a1fe9de3995ffcb374c9124";
export default node;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<76980bd62a1a415efbb27298d27874da>>
* @generated SignedSource<<3d1854151a950f0b4b85faf16c995c61>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -9,8 +9,8 @@
// @ts-nocheck
import { ReaderFragment } from 'relay-runtime';
export type PolicyStatus = "DRAFT" | "PUBLISHED" | "%future added value";
export type PolicyVersionSignatureState = "REQUESTED" | "SIGNED" | "%future added value";
export type PolicyStatus = "DRAFT" | "PUBLISHED";
export type PolicyVersionSignatureState = "REQUESTED" | "SIGNED";
import { FragmentRefs } from "relay-runtime";
export type PoliciesPageRowFragment$data = {
readonly description: string;

View File

@@ -1,135 +0,0 @@
/**
* @generated SignedSource<<0c65ab6f2531f65b9687e3ca13074774>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type PolicyStatus = "DRAFT" | "PUBLISHED" | "%future added value";
export type CreateDraftPolicyVersionInput = {
policyID: string;
};
export type PolicyPageCreateDraftPolicyVersionMutation$variables = {
input: CreateDraftPolicyVersionInput;
};
export type PolicyPageCreateDraftPolicyVersionMutation$data = {
readonly createDraftPolicyVersion: {
readonly policyVersionEdge: {
readonly node: {
readonly id: string;
readonly status: PolicyStatus;
readonly version: number;
};
};
};
};
export type PolicyPageCreateDraftPolicyVersionMutation = {
response: PolicyPageCreateDraftPolicyVersionMutation$data;
variables: PolicyPageCreateDraftPolicyVersionMutation$variables;
};
const node: ConcreteRequest = (function(){
var v0 = [
{
"defaultValue": null,
"kind": "LocalArgument",
"name": "input"
}
],
v1 = [
{
"alias": null,
"args": [
{
"kind": "Variable",
"name": "input",
"variableName": "input"
}
],
"concreteType": "CreateDraftPolicyVersionPayload",
"kind": "LinkedField",
"name": "createDraftPolicyVersion",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "PolicyVersionEdge",
"kind": "LinkedField",
"name": "policyVersionEdge",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "PolicyVersion",
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "id",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "version",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "status",
"storageKey": null
}
],
"storageKey": null
}
],
"storageKey": null
}
],
"storageKey": null
}
];
return {
"fragment": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Fragment",
"metadata": null,
"name": "PolicyPageCreateDraftPolicyVersionMutation",
"selections": (v1/*: any*/),
"type": "Mutation",
"abstractKey": null
},
"kind": "Request",
"operation": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Operation",
"name": "PolicyPageCreateDraftPolicyVersionMutation",
"selections": (v1/*: any*/)
},
"params": {
"cacheID": "197f53d67c5393a8163be2a8ce356cd0",
"id": null,
"metadata": {},
"name": "PolicyPageCreateDraftPolicyVersionMutation",
"operationKind": "mutation",
"text": "mutation PolicyPageCreateDraftPolicyVersionMutation(\n $input: CreateDraftPolicyVersionInput!\n) {\n createDraftPolicyVersion(input: $input) {\n policyVersionEdge {\n node {\n id\n version\n status\n }\n }\n }\n}\n"
}
};
})();
(node as any).hash = "7fcf72158c1dc58ae8f9d7efaa6d93a9";
export default node;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<769555856fa8bff973c4685249abbb2f>>
* @generated SignedSource<<088b04713ff854ffccb30532513bb093>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -9,8 +9,8 @@
// @ts-nocheck
import { ReaderFragment } from 'relay-runtime';
export type PolicyStatus = "DRAFT" | "PUBLISHED" | "%future added value";
export type PolicyVersionSignatureState = "REQUESTED" | "SIGNED" | "%future added value";
export type PolicyStatus = "DRAFT" | "PUBLISHED";
export type PolicyVersionSignatureState = "REQUESTED" | "SIGNED";
import { FragmentRefs } from "relay-runtime";
export type PolicyPagePolicyFragment$data = {
readonly id: string;
@@ -27,17 +27,22 @@ export type PolicyPagePolicyFragment$data = {
readonly id: string;
readonly publishedAt: any | null | undefined;
readonly signatures: {
readonly __id: string;
readonly edges: ReadonlyArray<{
readonly node: {
readonly id: string;
readonly signedBy: {
readonly id: string;
};
readonly state: PolicyVersionSignatureState;
readonly " $fragmentSpreads": FragmentRefs<"PolicySignaturesDialog_signature">;
};
}>;
};
readonly status: PolicyStatus;
readonly updatedAt: any;
readonly version: number;
readonly " $fragmentSpreads": FragmentRefs<"PolicyVersionHistoryDialogFragment">;
readonly " $fragmentSpreads": FragmentRefs<"PolicySignaturesDialog_version" | "PolicyVersionHistoryDialogFragment">;
};
}>;
};
@@ -55,12 +60,69 @@ var v0 = {
"kind": "ScalarField",
"name": "id",
"storageKey": null
},
v1 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "__typename",
"storageKey": null
},
v2 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "cursor",
"storageKey": null
},
v3 = {
"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
},
v4 = {
"kind": "ClientExtension",
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "__id",
"storageKey": null
}
]
};
return {
"argumentDefinitions": [],
"kind": "Fragment",
"metadata": {
"connection": [
{
"count": null,
"cursor": null,
"direction": "forward",
"path": null
},
{
"count": null,
"cursor": null,
@@ -161,17 +223,11 @@ return {
"storageKey": null
},
{
"alias": null,
"args": [
{
"kind": "Literal",
"name": "first",
"value": 100
}
],
"alias": "signatures",
"args": null,
"concreteType": "PolicyVersionSignatureConnection",
"kind": "LinkedField",
"name": "signatures",
"name": "__PolicyPage_signatures_connection",
"plural": false,
"selections": [
{
@@ -197,15 +253,36 @@ return {
"kind": "ScalarField",
"name": "state",
"storageKey": null
}
},
{
"alias": null,
"args": null,
"concreteType": "People",
"kind": "LinkedField",
"name": "signedBy",
"plural": false,
"selections": [
(v0/*: any*/)
],
"storageKey": null
},
{
"args": null,
"kind": "FragmentSpread",
"name": "PolicySignaturesDialog_signature"
},
(v1/*: any*/)
],
"storageKey": null
}
},
(v2/*: any*/)
],
"storageKey": null
}
},
(v3/*: any*/),
(v4/*: any*/)
],
"storageKey": "signatures(first:100)"
"storageKey": null
},
{
"args": null,
@@ -213,62 +290,20 @@ return {
"name": "PolicyVersionHistoryDialogFragment"
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "__typename",
"storageKey": null
}
"kind": "FragmentSpread",
"name": "PolicySignaturesDialog_version"
},
(v1/*: any*/)
],
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "cursor",
"storageKey": null
}
(v2/*: any*/)
],
"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
}
]
}
(v3/*: any*/),
(v4/*: any*/)
],
"storageKey": null
}
@@ -278,6 +313,6 @@ return {
};
})();
(node as any).hash = "684db4b721c50638ee5ec1f9f4ebdd16";
(node as any).hash = "b53b22a05e1abd608ecd688b6a9d5bad";
export default node;

View File

@@ -0,0 +1,267 @@
import {
Avatar,
Badge,
Button,
Dialog,
DialogContent,
DialogFooter,
DialogTitle,
IconCircleCheck,
IconClock,
PolicyVersionBadge,
Spinner,
} from "@probo/ui";
import { useTranslate } from "@probo/i18n";
import { useState, type ReactNode, Suspense } from "react";
import type { PolicyPagePolicyFragment$data } from "../__generated__/PolicyPagePolicyFragment.graphql";
import clsx from "clsx";
import type { ItemOf, NodeOf } from "/types";
import { graphql, useFragment } from "react-relay";
import type { PolicySignaturesDialog_version$key } from "./__generated__/PolicySignaturesDialog_version.graphql";
import type { PolicySignaturesDialog_signature$key } from "./__generated__/PolicySignaturesDialog_signature.graphql";
import { usePeople } from "/hooks/graph/PeopleGraph.ts";
import { useOrganizationId } from "/hooks/useOrganizationId.ts";
import { useMutationWithToasts } from "/hooks/useMutationWithToasts.ts";
import { sprintf } from "@probo/helpers";
type Props = {
policy: PolicyPagePolicyFragment$data;
children?: ReactNode;
};
type Version = NodeOf<PolicyPagePolicyFragment$data["versions"]>;
export function PolicySignaturesDialog(props: Props) {
const { __ } = useTranslate();
const versions = props.policy.versions.edges.map((edge) => edge.node);
const [selectedVersionId, setSelectedVersionId] = useState<string>(
versions[0].id
);
const selectedVersion = versions.find((v) => v.id === selectedVersionId);
return (
<Dialog trigger={props.children}>
<DialogContent className="flex" scrollableChildren>
<aside className="p-6 overflow-y-auto w-60 flex-none space-y-2">
<div className="text-base text-txt-primary font-medium mb-4">
{__("Version History")}
</div>
{versions.map((version) => (
<VersionItem
key={version.id}
version={version}
active={selectedVersionId === version.id}
onSelect={setSelectedVersionId}
/>
))}
</aside>
<main className="flex-1 px-12 py-8">
<DialogTitle className="text-2xl font-bold text-txt-primary mb-4">
{__("Signatures")}
</DialogTitle>
<p className="text-sm text-txt-secondary mb-4">
{__(
"Click request to ask for a signature from people in your organization."
)}
</p>
<Suspense fallback={<Spinner centered />}>
<SignatureList version={selectedVersion} />
</Suspense>
</main>
</DialogContent>
<DialogFooter></DialogFooter>
</Dialog>
);
}
const versionFragment = graphql`
fragment PolicySignaturesDialog_version on PolicyVersion {
version
status
publishedAt
updatedAt
}
`;
function VersionItem(props: {
version: Version;
active?: boolean;
onSelect: (v: string) => void;
}) {
const { dateTimeFormat, __ } = useTranslate();
const version = useFragment<PolicySignaturesDialog_version$key>(
versionFragment,
props.version
);
return (
<button
onClick={() => props.onSelect(props.version.id)}
className={clsx(
"text-start space-y-1 w-full overflow-hidden py-2 px-3 w-full hover:bg-tertiary-hover cursor-pointer rounded",
props.active && "bg-tertiary-pressed"
)}
>
<div className="flex gap-1 text-sm text-txt-primary">
<span>
{__("Version")} {version.version}
</span>
<PolicyVersionBadge state={version.status} />
</div>
<div className="text-xs text-txt-secondary">
{dateTimeFormat(version.publishedAt ?? version.updatedAt)}
</div>
</button>
);
}
function SignatureList(props: { version: Version }) {
const signatures = props.version.signatures.edges.map((edge) => edge.node);
const { __ } = useTranslate();
const signatureMap = new Map(signatures.map((s) => [s.signedBy.id, s]));
const organizationId = useOrganizationId();
const people = usePeople(organizationId);
if (people.length === 0) {
return (
<div className="text-center text-sm text-txt-tertiary py-3">
{__("No people available to request signatures from")}
</div>
);
}
return (
<div className="space-y-2 divide-y divide-border-solid">
{people.map((p) => (
<SignatureItem
key={p.id}
versionId={props.version.id}
signature={signatureMap.get(p.id)}
people={p}
connectionId={props.version.signatures.__id}
/>
))}
</div>
);
}
/**
* Fragments
*/
const signatureFragment = graphql`
fragment PolicySignaturesDialog_signature on PolicyVersionSignature {
id
state
signedAt
requestedAt
signedBy {
fullName
primaryEmailAddress
}
}
`;
/**
* Mutations
*/
const requestSignatureMutation = graphql`
mutation PolicySignaturesDialog_requestSignatureMutation(
$input: RequestSignatureInput!
$connections: [ID!]!
) {
requestSignature(input: $input) {
policyVersionSignatureEdge @prependEdge(connections: $connections) {
node {
id
state
signedBy {
id
}
...PolicySignaturesDialog_signature
}
}
}
}
`;
function SignatureItem(props: {
versionId: string;
signature?: PolicySignaturesDialog_signature$key;
people: ItemOf<ReturnType<typeof usePeople>>;
connectionId: string;
}) {
const signature = useFragment(signatureFragment, props.signature);
const { __, dateTimeFormat } = useTranslate();
const [requestSignature, isSendingRequest] = useMutationWithToasts(
requestSignatureMutation,
{
successMessage: __("Signature request sent successfully"),
errorMessage: __("Failed to send signature request"),
}
);
// No signature request for this user
if (!signature) {
return (
<div className="flex gap-3 items-center py-3">
<Avatar size="l" name={props.people.fullName} />
<div className="space-y-1">
<div className="text-sm text-txt-primary font-medium">
{props.people.fullName}
</div>
<div className="text-xs text-txt-secondary">
{props.people.primaryEmailAddress}
</div>
</div>
<Button
variant="secondary"
className="ml-auto"
disabled={isSendingRequest}
onClick={() => {
requestSignature({
variables: {
input: {
policyVersionId: props.versionId,
signatoryId: props.people.id,
},
connections: [props.connectionId],
},
});
}}
>
{__("Request signature")}
</Button>
</div>
);
}
const isSigned = signature.state === "SIGNED";
const label = isSigned ? __("Signed on %s") : __("Requested on %s");
return (
<div className="flex gap-3 items-center py-3">
<Avatar size="l" name={signature.signedBy.fullName} />
<div className="space-y-1">
<div className="text-sm text-txt-primary font-medium">
{signature.signedBy.fullName}
</div>
<div className="text-xs text-txt-secondary flex items-center gap-1">
{isSigned ? (
<IconCircleCheck size={16} className="text-txt-accent" />
) : (
<IconClock size={16} />
)}
<span>
{sprintf(
label,
dateTimeFormat(
isSigned ? signature.signedAt : signature.requestedAt
)
)}
</span>
</div>
</div>
<Badge variant={isSigned ? "success" : "warning"} className="ml-auto">
{isSigned ? __("Signed") : __("Pending")}
</Badge>
</div>
);
}

View File

@@ -9,15 +9,9 @@ import {
import { graphql } from "relay-runtime";
import { useTranslate } from "@probo/i18n";
import { useState, type ReactNode } from "react";
import type {
PolicyPagePolicyFragment$data,
PolicyPagePolicyFragment$key,
} from "../__generated__/PolicyPagePolicyFragment.graphql";
import type { PolicyPagePolicyFragment$data } from "../__generated__/PolicyPagePolicyFragment.graphql";
import { useFragment } from "react-relay";
import type {
PolicyVersionHistoryDialogFragment$data,
PolicyVersionHistoryDialogFragment$key,
} from "./__generated__/PolicyVersionHistoryDialogFragment.graphql";
import type { PolicyVersionHistoryDialogFragment$key } from "./__generated__/PolicyVersionHistoryDialogFragment.graphql";
import clsx from "clsx";
import type { NodeOf } from "/types";
@@ -48,15 +42,16 @@ export function PolicyVersionHistoryDialog(props: Props) {
const versions = props.policy.versions.edges.map((edge) => edge.node);
const [selectedVersion, setSelectedVersion] = useState<Version>(versions[0]);
return (
<Dialog defaultOpen trigger={props.children}>
<Dialog trigger={props.children}>
<DialogContent className="flex" scrollableChildren>
<aside className="p-6 overflow-y-auto w-60 flex-none">
<aside className="p-6 overflow-y-auto w-60 flex-none space-y-2">
<DialogTitle className="text-base text-txt-primary font-medium mb-4">
{__("Version History")}
</DialogTitle>
{versions.map((version) => (
<VersionItem
key={version.id}
policy={props.policy}
version={version}
active={selectedVersion === version}
onSelect={setSelectedVersion}
@@ -67,12 +62,13 @@ export function PolicyVersionHistoryDialog(props: Props) {
<Markdown content={selectedVersion.content} />
</main>
</DialogContent>
<DialogFooter></DialogFooter>
<DialogFooter />
</Dialog>
);
}
function VersionItem(props: {
policy: PolicyPagePolicyFragment$data;
version: Version;
active?: boolean;
onSelect: (v: Version) => void;
@@ -81,26 +77,25 @@ function VersionItem(props: {
historyFragment,
props.version
);
const { dateFormat } = useTranslate();
const { dateTimeFormat } = useTranslate();
return (
<button
onClick={() => props.onSelect(props.version)}
className={clsx(
"flex items-center gap-1 py-2 px-[10px] w-full hover:bg-tertiary-hover cursor-pointer rounded",
"flex items-center gap-2 py-2 px-[10px] w-full hover:bg-tertiary-hover cursor-pointer rounded",
props.active && "bg-tertiary-pressed"
)}
>
<Avatar name={version.publishedBy?.fullName ?? ""} size="l" />
<div className="space-y-[2px]">
<div className="text-sm text-txt-primary">
{version.publishedBy?.fullName ?? "Anonymous"}
<Avatar
name={version.publishedBy?.fullName ?? props.policy.owner.fullName}
size="l"
/>
<div className="text-start space-y-[2px] w-full overflow-hidden">
<div className="text-sm text-txt-primary whitespace-nowrap overflow-hidden text-ellipsis">
{version.publishedBy?.fullName ?? props.policy.owner.fullName}
</div>
<div className="text-xs text-txt-secondary">
{dateFormat(version.publishedAt, {
day: "numeric",
month: "short",
year: "numeric",
})}
<div className="text-xs text-txt-secondary whitespace-nowrap overflow-hidden text-ellipsis">
{dateTimeFormat(version.publishedAt ?? version.updatedAt)}
</div>
</div>
</button>

View File

@@ -0,0 +1,254 @@
/**
* @generated SignedSource<<af123c927212dd99fbaeb81e3b2f2bd9>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
import { FragmentRefs } from "relay-runtime";
export type PolicyVersionSignatureState = "REQUESTED" | "SIGNED";
export type RequestSignatureInput = {
policyVersionId: string;
signatoryId: string;
};
export type PolicySignaturesDialog_requestSignatureMutation$variables = {
connections: ReadonlyArray<string>;
input: RequestSignatureInput;
};
export type PolicySignaturesDialog_requestSignatureMutation$data = {
readonly requestSignature: {
readonly policyVersionSignatureEdge: {
readonly node: {
readonly id: string;
readonly signedBy: {
readonly id: string;
};
readonly state: PolicyVersionSignatureState;
readonly " $fragmentSpreads": FragmentRefs<"PolicySignaturesDialog_signature">;
};
};
};
};
export type PolicySignaturesDialog_requestSignatureMutation = {
response: PolicySignaturesDialog_requestSignatureMutation$data;
variables: PolicySignaturesDialog_requestSignatureMutation$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 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "state",
"storageKey": null
};
return {
"fragment": {
"argumentDefinitions": [
(v0/*: any*/),
(v1/*: any*/)
],
"kind": "Fragment",
"metadata": null,
"name": "PolicySignaturesDialog_requestSignatureMutation",
"selections": [
{
"alias": null,
"args": (v2/*: any*/),
"concreteType": "RequestSignaturePayload",
"kind": "LinkedField",
"name": "requestSignature",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "PolicyVersionSignatureEdge",
"kind": "LinkedField",
"name": "policyVersionSignatureEdge",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "PolicyVersionSignature",
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v3/*: any*/),
(v4/*: any*/),
{
"alias": null,
"args": null,
"concreteType": "People",
"kind": "LinkedField",
"name": "signedBy",
"plural": false,
"selections": [
(v3/*: any*/)
],
"storageKey": null
},
{
"args": null,
"kind": "FragmentSpread",
"name": "PolicySignaturesDialog_signature"
}
],
"storageKey": null
}
],
"storageKey": null
}
],
"storageKey": null
}
],
"type": "Mutation",
"abstractKey": null
},
"kind": "Request",
"operation": {
"argumentDefinitions": [
(v1/*: any*/),
(v0/*: any*/)
],
"kind": "Operation",
"name": "PolicySignaturesDialog_requestSignatureMutation",
"selections": [
{
"alias": null,
"args": (v2/*: any*/),
"concreteType": "RequestSignaturePayload",
"kind": "LinkedField",
"name": "requestSignature",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "PolicyVersionSignatureEdge",
"kind": "LinkedField",
"name": "policyVersionSignatureEdge",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "PolicyVersionSignature",
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v3/*: any*/),
(v4/*: any*/),
{
"alias": null,
"args": null,
"concreteType": "People",
"kind": "LinkedField",
"name": "signedBy",
"plural": false,
"selections": [
(v3/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "fullName",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "primaryEmailAddress",
"storageKey": null
}
],
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "signedAt",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "requestedAt",
"storageKey": null
}
],
"storageKey": null
}
],
"storageKey": null
},
{
"alias": null,
"args": null,
"filters": null,
"handle": "prependEdge",
"key": "",
"kind": "LinkedHandle",
"name": "policyVersionSignatureEdge",
"handleArgs": [
{
"kind": "Variable",
"name": "connections",
"variableName": "connections"
}
]
}
],
"storageKey": null
}
]
},
"params": {
"cacheID": "20c460adbc3480622c4c91220a6e54fe",
"id": null,
"metadata": {},
"name": "PolicySignaturesDialog_requestSignatureMutation",
"operationKind": "mutation",
"text": "mutation PolicySignaturesDialog_requestSignatureMutation(\n $input: RequestSignatureInput!\n) {\n requestSignature(input: $input) {\n policyVersionSignatureEdge {\n node {\n id\n state\n signedBy {\n id\n }\n ...PolicySignaturesDialog_signature\n }\n }\n }\n}\n\nfragment PolicySignaturesDialog_signature on PolicyVersionSignature {\n id\n state\n signedAt\n requestedAt\n signedBy {\n fullName\n primaryEmailAddress\n id\n }\n}\n"
}
};
})();
(node as any).hash = "24825b1c4040debb7edda76c8ad3d662";
export default node;

View File

@@ -0,0 +1,96 @@
/**
* @generated SignedSource<<4bda7ac839740680ea31b4c20299d9d4>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ReaderFragment } from 'relay-runtime';
export type PolicyVersionSignatureState = "REQUESTED" | "SIGNED";
import { FragmentRefs } from "relay-runtime";
export type PolicySignaturesDialog_signature$data = {
readonly id: string;
readonly requestedAt: any;
readonly signedAt: any | null | undefined;
readonly signedBy: {
readonly fullName: string;
readonly primaryEmailAddress: string;
};
readonly state: PolicyVersionSignatureState;
readonly " $fragmentType": "PolicySignaturesDialog_signature";
};
export type PolicySignaturesDialog_signature$key = {
readonly " $data"?: PolicySignaturesDialog_signature$data;
readonly " $fragmentSpreads": FragmentRefs<"PolicySignaturesDialog_signature">;
};
const node: ReaderFragment = {
"argumentDefinitions": [],
"kind": "Fragment",
"metadata": null,
"name": "PolicySignaturesDialog_signature",
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "id",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "state",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "signedAt",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "requestedAt",
"storageKey": null
},
{
"alias": null,
"args": null,
"concreteType": "People",
"kind": "LinkedField",
"name": "signedBy",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "fullName",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "primaryEmailAddress",
"storageKey": null
}
],
"storageKey": null
}
],
"type": "PolicyVersionSignature",
"abstractKey": null
};
(node as any).hash = "b2e435dc6db3a0df223af98251adcb30";
export default node;

View File

@@ -0,0 +1,67 @@
/**
* @generated SignedSource<<efbbec863b7456c271e408fb553f9ea1>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ReaderFragment } from 'relay-runtime';
export type PolicyStatus = "DRAFT" | "PUBLISHED";
import { FragmentRefs } from "relay-runtime";
export type PolicySignaturesDialog_version$data = {
readonly publishedAt: any | null | undefined;
readonly status: PolicyStatus;
readonly updatedAt: any;
readonly version: number;
readonly " $fragmentType": "PolicySignaturesDialog_version";
};
export type PolicySignaturesDialog_version$key = {
readonly " $data"?: PolicySignaturesDialog_version$data;
readonly " $fragmentSpreads": FragmentRefs<"PolicySignaturesDialog_version">;
};
const node: ReaderFragment = {
"argumentDefinitions": [],
"kind": "Fragment",
"metadata": null,
"name": "PolicySignaturesDialog_version",
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "version",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "status",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "publishedAt",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "updatedAt",
"storageKey": null
}
],
"type": "PolicyVersion",
"abstractKey": null
};
(node as any).hash = "cdec8e3244fd6329a86b5825a4133c44";
export default node;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<929e2bdd729e8b9654b27cd9e9dbccb2>>
* @generated SignedSource<<45feb21e3f613b3818af04725831a427>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -9,7 +9,7 @@
// @ts-nocheck
import { ReaderFragment } from 'relay-runtime';
export type PolicyStatus = "DRAFT" | "PUBLISHED" | "%future added value";
export type PolicyStatus = "DRAFT" | "PUBLISHED";
import { FragmentRefs } from "relay-runtime";
export type PolicyVersionHistoryDialogFragment$data = {
readonly changelog: string;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<e90085131e94bcac0f94ecf5b1329712>>
* @generated SignedSource<<7b7cbab5e73007ace1e7d07970b633e8>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -9,8 +9,8 @@
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type PolicyStatus = "DRAFT" | "PUBLISHED" | "%future added value";
export type PolicyVersionSignatureState = "REQUESTED" | "SIGNED" | "%future added value";
export type PolicyStatus = "DRAFT" | "PUBLISHED";
export type PolicyVersionSignatureState = "REQUESTED" | "SIGNED";
export type CreateDraftPolicyVersionInput = {
policyID: string;
};

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<05dd3d535fb39d12fc0c015864a85d31>>
* @generated SignedSource<<7d5730a1c721f5256e6e0c07199aa0d2>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -10,7 +10,7 @@
import { ConcreteRequest } from 'relay-runtime';
import { FragmentRefs } from "relay-runtime";
export type RiskTreatment = "ACCEPTED" | "AVOIDED" | "MITIGATED" | "TRANSFERRED" | "%future added value";
export type RiskTreatment = "ACCEPTED" | "AVOIDED" | "MITIGATED" | "TRANSFERRED";
export type CreateRiskInput = {
category: string;
description: string;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<71792ccd18aa886a5df0ecfa8a23fda9>>
* @generated SignedSource<<dc5427f9197a47414ed10738473fdca1>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -10,7 +10,7 @@
import { ConcreteRequest } from 'relay-runtime';
import { FragmentRefs } from "relay-runtime";
export type RiskTreatment = "ACCEPTED" | "AVOIDED" | "MITIGATED" | "TRANSFERRED" | "%future added value";
export type RiskTreatment = "ACCEPTED" | "AVOIDED" | "MITIGATED" | "TRANSFERRED";
export type UpdateRiskInput = {
category?: string | null | undefined;
description?: string | null | undefined;

View File

@@ -1,262 +0,0 @@
/**
* @generated SignedSource<<cfb39c3c814ed520de659842d6fffe38>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
import { FragmentRefs } from "relay-runtime";
export type RiskTreatment = "ACCEPTED" | "AVOIDED" | "MITIGATED" | "TRANSFERRED" | "%future added value";
export type RiskDetailPageQuery$variables = {
riskId: string;
};
export type RiskDetailPageQuery$data = {
readonly node: {
readonly createdAt?: any;
readonly description?: string;
readonly inherentImpact?: number;
readonly inherentLikelihood?: number;
readonly name?: string;
readonly note?: string;
readonly owner?: {
readonly fullName: string;
readonly id: string;
} | null | undefined;
readonly residualImpact?: number;
readonly residualLikelihood?: number;
readonly treatment?: RiskTreatment;
readonly updatedAt?: any;
readonly " $fragmentSpreads": FragmentRefs<"useRiskFormFragment">;
};
};
export type RiskDetailPageQuery = {
response: RiskDetailPageQuery$data;
variables: RiskDetailPageQuery$variables;
};
const node: ConcreteRequest = (function(){
var v0 = [
{
"defaultValue": null,
"kind": "LocalArgument",
"name": "riskId"
}
],
v1 = [
{
"kind": "Variable",
"name": "id",
"variableName": "riskId"
}
],
v2 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "name",
"storageKey": null
},
v3 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "description",
"storageKey": null
},
v4 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "treatment",
"storageKey": null
},
v5 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "id",
"storageKey": null
},
v6 = {
"alias": null,
"args": null,
"concreteType": "People",
"kind": "LinkedField",
"name": "owner",
"plural": false,
"selections": [
(v5/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "fullName",
"storageKey": null
}
],
"storageKey": null
},
v7 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "inherentLikelihood",
"storageKey": null
},
v8 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "inherentImpact",
"storageKey": null
},
v9 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "residualLikelihood",
"storageKey": null
},
v10 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "residualImpact",
"storageKey": null
},
v11 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "note",
"storageKey": null
},
v12 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "createdAt",
"storageKey": null
},
v13 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "updatedAt",
"storageKey": null
};
return {
"fragment": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Fragment",
"metadata": null,
"name": "RiskDetailPageQuery",
"selections": [
{
"alias": null,
"args": (v1/*: any*/),
"concreteType": null,
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
{
"kind": "InlineFragment",
"selections": [
(v2/*: any*/),
(v3/*: any*/),
(v4/*: any*/),
(v6/*: any*/),
(v7/*: any*/),
(v8/*: any*/),
(v9/*: any*/),
(v10/*: any*/),
(v11/*: any*/),
(v12/*: any*/),
(v13/*: any*/),
{
"args": null,
"kind": "FragmentSpread",
"name": "useRiskFormFragment"
}
],
"type": "Risk",
"abstractKey": null
}
],
"storageKey": null
}
],
"type": "Query",
"abstractKey": null
},
"kind": "Request",
"operation": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Operation",
"name": "RiskDetailPageQuery",
"selections": [
{
"alias": null,
"args": (v1/*: any*/),
"concreteType": null,
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "__typename",
"storageKey": null
},
(v5/*: any*/),
{
"kind": "InlineFragment",
"selections": [
(v2/*: any*/),
(v3/*: any*/),
(v4/*: any*/),
(v6/*: any*/),
(v7/*: any*/),
(v8/*: any*/),
(v9/*: any*/),
(v10/*: any*/),
(v11/*: any*/),
(v12/*: any*/),
(v13/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "category",
"storageKey": null
}
],
"type": "Risk",
"abstractKey": null
}
],
"storageKey": null
}
]
},
"params": {
"cacheID": "0ed24be13c829da91705d506a46eb562",
"id": null,
"metadata": {},
"name": "RiskDetailPageQuery",
"operationKind": "query",
"text": "query RiskDetailPageQuery(\n $riskId: ID!\n) {\n node(id: $riskId) {\n __typename\n ... on Risk {\n name\n description\n treatment\n owner {\n id\n fullName\n }\n inherentLikelihood\n inherentImpact\n residualLikelihood\n residualImpact\n note\n createdAt\n updatedAt\n ...useRiskFormFragment\n }\n id\n }\n}\n\nfragment useRiskFormFragment on Risk {\n id\n name\n category\n description\n treatment\n inherentLikelihood\n inherentImpact\n residualLikelihood\n residualImpact\n note\n owner {\n id\n }\n}\n"
}
};
})();
(node as any).hash = "94de027c557f9b633765f9dde7c0aae1";
export default node;

View File

@@ -1,132 +0,0 @@
/**
* @generated SignedSource<<b410e2d0b99401f7ad86c1d4f7a0fcfe>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type DeleteRiskInput = {
riskId: string;
};
export type RisksPageDeleteMutation$variables = {
connections: ReadonlyArray<string>;
input: DeleteRiskInput;
};
export type RisksPageDeleteMutation$data = {
readonly deleteRisk: {
readonly deletedRiskId: string;
};
};
export type RisksPageDeleteMutation = {
response: RisksPageDeleteMutation$data;
variables: RisksPageDeleteMutation$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": "deletedRiskId",
"storageKey": null
};
return {
"fragment": {
"argumentDefinitions": [
(v0/*: any*/),
(v1/*: any*/)
],
"kind": "Fragment",
"metadata": null,
"name": "RisksPageDeleteMutation",
"selections": [
{
"alias": null,
"args": (v2/*: any*/),
"concreteType": "DeleteRiskPayload",
"kind": "LinkedField",
"name": "deleteRisk",
"plural": false,
"selections": [
(v3/*: any*/)
],
"storageKey": null
}
],
"type": "Mutation",
"abstractKey": null
},
"kind": "Request",
"operation": {
"argumentDefinitions": [
(v1/*: any*/),
(v0/*: any*/)
],
"kind": "Operation",
"name": "RisksPageDeleteMutation",
"selections": [
{
"alias": null,
"args": (v2/*: any*/),
"concreteType": "DeleteRiskPayload",
"kind": "LinkedField",
"name": "deleteRisk",
"plural": false,
"selections": [
(v3/*: any*/),
{
"alias": null,
"args": null,
"filters": null,
"handle": "deleteEdge",
"key": "",
"kind": "ScalarHandle",
"name": "deletedRiskId",
"handleArgs": [
{
"kind": "Variable",
"name": "connections",
"variableName": "connections"
}
]
}
],
"storageKey": null
}
]
},
"params": {
"cacheID": "a08977dc1d81b5ab63e431014b2d2b41",
"id": null,
"metadata": {},
"name": "RisksPageDeleteMutation",
"operationKind": "mutation",
"text": "mutation RisksPageDeleteMutation(\n $input: DeleteRiskInput!\n) {\n deleteRisk(input: $input) {\n deletedRiskId\n }\n}\n"
}
};
})();
(node as any).hash = "fb17a17779665063283d3d73fcb39785";
export default node;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<83a07ae53aaa5005db49f5ea8b4479c6>>
* @generated SignedSource<<62ef6617862f528f26edaffe3adc3069>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -9,7 +9,7 @@
// @ts-nocheck
import { ReaderFragment } from 'relay-runtime';
export type MeasureState = "IMPLEMENTED" | "IN_PROGRESS" | "NOT_APPLICABLE" | "NOT_STARTED" | "%future added value";
export type MeasureState = "IMPLEMENTED" | "IN_PROGRESS" | "NOT_APPLICABLE" | "NOT_STARTED";
import { FragmentRefs } from "relay-runtime";
export type RiskMeasuresTabFragment$data = {
readonly id: string;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<ddd22d66836f5fd323ddf25475dd6d72>>
* @generated SignedSource<<2de1bbc55eb7b45a9546c8ec306ad979>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -9,8 +9,8 @@
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type BusinessImpact = "CRITICAL" | "HIGH" | "LOW" | "MEDIUM" | "%future added value";
export type DataSensitivity = "CRITICAL" | "HIGH" | "LOW" | "MEDIUM" | "NONE" | "%future added value";
export type BusinessImpact = "CRITICAL" | "HIGH" | "LOW" | "MEDIUM";
export type DataSensitivity = "CRITICAL" | "HIGH" | "LOW" | "MEDIUM" | "NONE";
export type VendorsPageQuery$variables = {
organizationId: string;
};

View File

@@ -50,26 +50,41 @@ export function TranslatorProvider({
export function useTranslate() {
const { translate, lang } = useContext(TranslatorContext);
const dateFormat = (
date: Date | string | null | undefined,
options: Intl.DateTimeFormatOptions = {
year: "numeric",
month: "short",
day: "numeric",
weekday: "short",
}
) => {
if (!date) {
return "";
}
if (typeof date === "string") {
return new Intl.DateTimeFormat(lang, options).format(parseDate(date));
}
return new Intl.DateTimeFormat(lang, options).format(date);
};
return {
lang,
__: translate,
dateFormat: (
dateFormat: dateFormat,
dateTimeFormat: (
date: Date | string | null | undefined,
options: Intl.DateTimeFormatOptions = {
year: "numeric",
month: "short",
hour: "2-digit",
hour12: false,
minute: "2-digit",
day: "numeric",
weekday: "short",
month: "short",
year: "numeric",
}
) => {
if (!date) {
return "";
}
if (typeof date === "string") {
return new Intl.DateTimeFormat(lang, options).format(parseDate(date));
}
return new Intl.DateTimeFormat(lang, options).format(date);
return dateFormat(date, options);
},
};
}

View File

@@ -2,7 +2,6 @@ import type {IconProps} from "./type.ts";
export function IconCircleCheck({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">
<rect x="6" y="6" width="12" height="12" fill="currentColor"/>
<path fillRule="evenodd" clipRule="evenodd" d="M12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2ZM15.774 10.1333C16.1237 9.70582 16.0607 9.0758 15.6332 8.72607C15.2058 8.37635 14.5758 8.43935 14.226 8.86679L10.4258 13.5116L9.20711 12.2929C8.81658 11.9024 8.18342 11.9024 7.79289 12.2929C7.40237 12.6834 7.40237 13.3166 7.79289 13.7071L9.79289 15.7071C9.99267 15.9069 10.2676 16.0129 10.5498 15.9988C10.832 15.9847 11.095 15.8519 11.274 15.6333L15.774 10.1333Z" fill="currentColor"/>
</svg>

View File

@@ -1,4 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="6" y="6" width="12" height="12" fill="#FFFFFB"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2ZM15.774 10.1333C16.1237 9.70582 16.0607 9.0758 15.6332 8.72607C15.2058 8.37635 14.5758 8.43935 14.226 8.86679L10.4258 13.5116L9.20711 12.2929C8.81658 11.9024 8.18342 11.9024 7.79289 12.2929C7.40237 12.6834 7.40237 13.3166 7.79289 13.7071L9.79289 15.7071C9.99267 15.9069 10.2676 16.0129 10.5498 15.9988C10.832 15.9847 11.095 15.8519 11.274 15.6333L15.774 10.1333Z" fill="#141E12"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2ZM15.774 10.1333C16.1237 9.70582 16.0607 9.0758 15.6332 8.72607C15.2058 8.37635 14.5758 8.43935 14.226 8.86679L10.4258 13.5116L9.20711 12.2929C8.81658 11.9024 8.18342 11.9024 7.79289 12.2929C7.40237 12.6834 7.40237 13.3166 7.79289 13.7071L9.79289 15.7071C9.99267 15.9069 10.2676 16.0129 10.5498 15.9988C10.832 15.9847 11.095 15.8519 11.274 15.6333L15.774 10.1333Z" fill="currentColor"/>
</svg>

Before

Width:  |  Height:  |  Size: 703 B

After

Width:  |  Height:  |  Size: 650 B

View File

@@ -9,15 +9,23 @@ type Toast = {
id: string;
};
const duration = 3000;
const useToasts = create(
combine({ toasts: [] as Toast[] }, (set) => ({
add: (toast: Pick<Toast, "title" | "description" | "variant">) =>
add: (toast: Pick<Toast, "title" | "description" | "variant">) => {
const id = Date.now().toString();
set((state) => ({
toasts: [
{ ...toast, id: Date.now().toString() },
...state.toasts,
],
})),
toasts: [{ ...toast, id }, ...state.toasts],
}));
setTimeout(() => {
set((state) => ({
toasts: [
...state.toasts.filter((toast) => toast.id !== id),
],
}));
}, duration);
},
remove: (id: string) =>
set((state) => ({
toasts: state.toasts.filter((toast) => toast.id !== id),
@@ -51,15 +59,32 @@ export function Toasts() {
}
const toast = tv({
base: "p-2 border rounded relative animate-in slide-in-from-right w-full cursor-pointer",
slots: {
wrapper:
"p-2 border rounded relative animate-in slide-in-from-right w-full cursor-pointer ",
timer: "absolute bottom-0 left-0 w-full h-1 starting:scale-x-0 scale-x-100 origin-left transition-all ease-linear",
},
variants: {
variant: {
success:
"bg-success text-invert text-txt-success border-border-success",
error: "bg-danger text-invert text-txt-danger border-border-danger",
warning:
"bg-warning text-invert text-txt-warning border-border-warning",
info: "bg-info text-invert text-txt-info border-border-info",
success: {
wrapper:
"bg-success text-invert text-txt-success border-border-success",
timer: "bg-border-success",
},
error: {
wrapper:
"bg-danger text-invert text-txt-danger border-border-danger",
timer: "bg-border-danger",
},
warning: {
wrapper:
"bg-warning text-invert text-txt-warning border-border-warning",
timer: "bg-border-warning",
},
info: {
wrapper: "bg-info text-invert text-txt-info border-border-info",
timer: "bg-border-info",
},
},
},
defaultVariants: {
@@ -68,10 +93,15 @@ const toast = tv({
});
export function Toast({ onClose, ...props }: Toast & { onClose: () => void }) {
const { wrapper, timer } = toast(props);
return (
<div className={toast(props)} onClick={onClose}>
<div className={wrapper()} onClick={onClose}>
<div className="font-semibold">{props.title}</div>
<div className="text-sm opacity-80">{props.description}</div>
<div
className={timer()}
style={{ transitionDuration: `${duration}ms` }}
/>
</div>
);
}

View File

@@ -0,0 +1,21 @@
import { PolicyVersionBadge } from "./PolicyVersionBadge";
import type { Meta, StoryObj } from "@storybook/react";
export default {
title: "Molecules/Badges/PolicyVersionBadge",
component: PolicyVersionBadge,
argTypes: {},
} satisfies Meta<typeof PolicyVersionBadge>;
type Story = StoryObj<typeof PolicyVersionBadge>;
export const Default: Story = {
render() {
return (
<div className="flex gap-2">
<PolicyVersionBadge state="DRAFT" />
<PolicyVersionBadge state="PUBLISHED" />
</div>
);
},
};

View File

@@ -0,0 +1,15 @@
import { useTranslate } from "@probo/i18n";
import { Badge } from "../../Atoms/Badge/Badge.tsx";
type Props = {
state: "DRAFT" | "PUBLISHED";
};
export function PolicyVersionBadge(props: Props) {
const { __ } = useTranslate();
return (
<Badge variant={props.state === "DRAFT" ? "neutral" : "success"}>
{props.state === "DRAFT" ? __("Draft") : __("Published")}
</Badge>
);
}

View File

@@ -17,7 +17,6 @@ import {
type ComponentProps,
type CSSProperties,
type HTMLAttributes,
type ReactElement,
type ReactNode,
type RefObject,
} from "react";
@@ -128,14 +127,16 @@ export function DialogFooter({
);
}
export function DialogContent(
props: HTMLAttributes<HTMLDivElement> & {
padded?: boolean;
scrollableChildren?: boolean;
},
) {
export function DialogContent({
padded,
scrollableChildren,
...props
}: HTMLAttributes<HTMLDivElement> & {
padded?: boolean;
scrollableChildren?: boolean;
}) {
let children = props.children;
if (props.scrollableChildren) {
if (scrollableChildren) {
children = Children.map(props.children, (c) => {
if (
isValidElement<{
@@ -162,7 +163,7 @@ export function DialogContent(
className={clsx(
"overflow-y-auto",
props.className,
props.padded && "p-6",
padded && "p-6",
)}
style={
{

View File

@@ -49,6 +49,7 @@ export {
} from "./Molecules/Dialog/Dialog";
export { RiskBadge } from "./Molecules/Badge/RiskBadge";
export { SeverityBadge } from "./Molecules/Badge/SeverityBadge.tsx";
export { PolicyVersionBadge } from "./Molecules/Badge/PolicyVersionBadge.tsx";
export {
ConfirmDialog,
useConfirmDialogRef,

View File

@@ -206,7 +206,7 @@
--color-dialog: #151715;
/* Borders */
--color-border-solid: #2b2f2a;
--color-border-solid: rgba(239, 254, 226, 0.08);
--color-border-low: rgba(239, 254, 226, 0.08);
--color-border-mid: rgba(233, 254, 223, 0.1);
--color-border-strong: rgba(241, 255, 237, 0.25);

View File

@@ -1,6 +0,0 @@
{
"src": "./apps/console2/src",
"schema": "./pkg/server/api/console/v1/schema.graphql",
"language": "typescript",
"eagerEsModules": true
}