From 6a97be5d8ee514433f9a36e7f5a2e67831a8f364 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20R=C3=A9?= Date: Mon, 29 Dec 2025 11:23:59 +0100 Subject: [PATCH] Replug custom domain setting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Émile Ré --- .../customDomains/CustomDomainManager.tsx | 155 ----------- .../DeleteCustomDomainDialog.tsx | 97 ------- .../domainSettingsPermissionsQuery.graphql.ts | 237 +++++++++++++++++ .../settings/DomainSettingsPage.tsx | 67 +++++ .../settings/DomainSettingsPageLoader.tsx | 38 +++ .../settings/DomainSettingsTab.tsx | 46 ---- .../DomainSettingsPageQuery.graphql.ts | 247 ++++++++++++++++++ .../DomainSettingsTabFragment.graphql.ts | 154 ----------- .../_components/DeleteDomainDialog.tsx | 109 ++++++++ .../settings/_components/DomainCard.tsx | 63 +++++ .../settings/_components/DomainDialog.tsx} | 92 +++---- .../settings/_components/NewDomainDialog.tsx} | 53 ++-- .../DeleteDomainDialogMutation.graphql.ts} | 24 +- .../DomainCardFragment.graphql.ts | 57 ++++ .../DomainDialogFragment.graphql.ts | 128 +++++++++ .../NewDomainDialogMutation.graphql.ts} | 24 +- apps/console/src/routes.tsx | 15 +- packages/helpers/src/customDomain.ts | 45 ++++ 18 files changed, 1085 insertions(+), 566 deletions(-) delete mode 100644 apps/console/src/components/customDomains/CustomDomainManager.tsx delete mode 100644 apps/console/src/components/customDomains/DeleteCustomDomainDialog.tsx create mode 100644 apps/console/src/pages/iam/organizations/settings/__generated__/domainSettingsPermissionsQuery.graphql.ts create mode 100644 apps/console/src/pages/organizations/settings/DomainSettingsPage.tsx create mode 100644 apps/console/src/pages/organizations/settings/DomainSettingsPageLoader.tsx delete mode 100644 apps/console/src/pages/organizations/settings/DomainSettingsTab.tsx create mode 100644 apps/console/src/pages/organizations/settings/__generated__/DomainSettingsPageQuery.graphql.ts delete mode 100644 apps/console/src/pages/organizations/settings/__generated__/DomainSettingsTabFragment.graphql.ts create mode 100644 apps/console/src/pages/organizations/settings/_components/DeleteDomainDialog.tsx create mode 100644 apps/console/src/pages/organizations/settings/_components/DomainCard.tsx rename apps/console/src/{components/customDomains/DomainDetailsDialog.tsx => pages/organizations/settings/_components/DomainDialog.tsx} (76%) rename apps/console/src/{components/customDomains/CreateCustomDomainDialog.tsx => pages/organizations/settings/_components/NewDomainDialog.tsx} (76%) rename apps/console/src/{components/customDomains/__generated__/CustomDomainManagerDeleteMutation.graphql.ts => pages/organizations/settings/_components/__generated__/DeleteDomainDialogMutation.graphql.ts} (65%) create mode 100644 apps/console/src/pages/organizations/settings/_components/__generated__/DomainCardFragment.graphql.ts create mode 100644 apps/console/src/pages/organizations/settings/_components/__generated__/DomainDialogFragment.graphql.ts rename apps/console/src/{components/customDomains/__generated__/CreateCustomDomainDialogMutation.graphql.ts => pages/organizations/settings/_components/__generated__/NewDomainDialogMutation.graphql.ts} (82%) create mode 100644 packages/helpers/src/customDomain.ts diff --git a/apps/console/src/components/customDomains/CustomDomainManager.tsx b/apps/console/src/components/customDomains/CustomDomainManager.tsx deleted file mode 100644 index 9650516db..000000000 --- a/apps/console/src/components/customDomains/CustomDomainManager.tsx +++ /dev/null @@ -1,155 +0,0 @@ -import { useTranslate } from "@probo/i18n"; -import { Button, Card, Badge, IconPlusLarge } from "@probo/ui"; -import { useMutationWithToasts } from "/hooks/useMutationWithToasts"; -import { graphql } from "relay-runtime"; -import type { CustomDomainManagerDeleteMutation } from "./__generated__/CustomDomainManagerDeleteMutation.graphql"; -import { CreateCustomDomainDialog } from "./CreateCustomDomainDialog"; -import { DeleteCustomDomainDialog } from "./DeleteCustomDomainDialog"; -import { DomainDetailsDialog } from "./DomainDetailsDialog"; -import { PermissionsContext } from "/providers/PermissionsContext"; -import { use } from "react"; - -const deleteCustomDomainMutation = graphql` - mutation CustomDomainManagerDeleteMutation($input: DeleteCustomDomainInput!) { - deleteCustomDomain(input: $input) { - deletedCustomDomainId - } - } -`; - -type CustomDomain = { - readonly id: string; - readonly domain: string; - readonly sslStatus: string; - readonly dnsRecords?: - | readonly { - readonly type: string; - readonly name: string; - readonly value: string; - readonly ttl?: number; - readonly purpose: string; - }[] - | null; - readonly createdAt?: string | null; - readonly updatedAt?: string | null; - readonly sslExpiresAt?: string | null; -}; - -interface CustomDomainManagerProps { - organizationId: string; - customDomain: CustomDomain | null | undefined; -} - -export function CustomDomainManager({ - organizationId, - customDomain, -}: CustomDomainManagerProps) { - const { __ } = useTranslate(); - const { isAuthorized } = use(PermissionsContext); - const [deleteCustomDomain] = - useMutationWithToasts( - deleteCustomDomainMutation, - { - successMessage: __("Domain deleted successfully"), - errorMessage: __("Failed to delete domain"), - } - ); - - const domain = customDomain; - - const getStatusBadge = (domain: CustomDomain) => { - if (domain.sslStatus === "ACTIVE") { - return {__("Active")}; - } - if ( - domain.sslStatus === "PROVISIONING" || - domain.sslStatus === "RENEWING" - ) { - return {__("Provisioning")}; - } - if (domain.sslStatus === "PENDING") { - return {__("Pending")}; - } - if (domain.sslStatus === "FAILED") { - return {__("Failed")}; - } - if (domain.sslStatus === "EXPIRED") { - return {__("Expired")}; - } - return {__("Unknown")}; - }; - - const handleDeleteDomain = async () => { - return deleteCustomDomain({ - variables: { - input: { organizationId }, - }, - updater: (store) => { - // Update the cache by setting customDomain to null - const organizationRecord = store.get(organizationId); - if (organizationRecord) { - organizationRecord.setValue(null, "customDomain"); - } - }, - }); - }; - - if (!domain) { - return ( - -
-

- {__("No custom domain configured")} -

-

- {__( - "Add your own domain to make your trust center more professional" - )} -

-
- {isAuthorized("Organization", "createCustomDomain") && ( - - - - )} -
-
-
- ); - } - - return ( - -
-
-
-
-
{domain.domain}
-
- {domain.sslStatus === "ACTIVE" - ? __("Verified") - : __("Pending verification")} -
-
- {getStatusBadge(domain)} -
- -
- - - - - {isAuthorized("CustomDomain", "deleteCustomDomain") && ( - - - - )} -
-
-
-
- ); -} diff --git a/apps/console/src/components/customDomains/DeleteCustomDomainDialog.tsx b/apps/console/src/components/customDomains/DeleteCustomDomainDialog.tsx deleted file mode 100644 index 240528e41..000000000 --- a/apps/console/src/components/customDomains/DeleteCustomDomainDialog.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import { useTranslate } from "@probo/i18n"; -import { - Button, - Dialog, - DialogContent, - DialogFooter, - Field, - useDialogRef, - IconTrashCan, -} from "@probo/ui"; -import { useState, useEffect, type ReactNode } from "react"; -import { sprintf } from "@probo/helpers"; - -interface DeleteCustomDomainDialogProps { - children: ReactNode; - domainName: string; - onConfirm: () => Promise; -} - -export function DeleteCustomDomainDialog({ - children, - domainName, - onConfirm, -}: DeleteCustomDomainDialogProps) { - const { __ } = useTranslate(); - const [inputValue, setInputValue] = useState(""); - const [isDeleting, setIsDeleting] = useState(false); - const dialogRef = useDialogRef(); - - const isConfirmDisabled = inputValue !== domainName || isDeleting; - - const handleConfirm = async () => { - if (inputValue === domainName && !isDeleting) { - setIsDeleting(true); - try { - await onConfirm(); - dialogRef.current?.close(); - } finally { - setIsDeleting(false); - } - } - }; - - useEffect(() => { - if (!isDeleting) { - setInputValue(""); - } - }, [isDeleting]); - - return ( - - -

- {sprintf( - __( - "This will permanently delete the custom domain %s and all its configuration." - ), - domainName - )} -

- -

- {__("This action cannot be undone.")} -

- - setInputValue(e.target.value)} - placeholder={domainName} - disabled={isDeleting} - autoComplete="off" - autoFocus - /> -
- - - -
- ); -} diff --git a/apps/console/src/pages/iam/organizations/settings/__generated__/domainSettingsPermissionsQuery.graphql.ts b/apps/console/src/pages/iam/organizations/settings/__generated__/domainSettingsPermissionsQuery.graphql.ts new file mode 100644 index 000000000..b00bc6780 --- /dev/null +++ b/apps/console/src/pages/iam/organizations/settings/__generated__/domainSettingsPermissionsQuery.graphql.ts @@ -0,0 +1,237 @@ +/** + * @generated SignedSource<<8bbf9386bee93021b7e06e5b3bd31c99>> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ConcreteRequest } from 'relay-runtime'; +export type MembershipRole = "ADMIN" | "AUDITOR" | "EMPLOYEE" | "OWNER" | "VIEWER"; +export type domainSettingsPermissionsQuery$variables = { + organizationId: string; +}; +export type domainSettingsPermissionsQuery$data = { + readonly organization: { + readonly __typename: "Organization"; + readonly viewerMembership: { + readonly role: MembershipRole; + }; + } | { + // This will never be '%other', but we need some + // value in case none of the concrete values match. + readonly __typename: "%other"; + }; + readonly viewer: { + readonly canCreateCustomDomain: boolean; + readonly canDeleteCustomDomain: boolean; + }; +}; +export type domainSettingsPermissionsQuery = { + response: domainSettingsPermissionsQuery$data; + variables: domainSettingsPermissionsQuery$variables; +}; + +const node: ConcreteRequest = (function(){ +var v0 = [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "organizationId" + } +], +v1 = { + "kind": "Variable", + "name": "id", + "variableName": "organizationId" +}, +v2 = { + "alias": "canCreateCustomDomain", + "args": [ + { + "kind": "Literal", + "name": "action", + "value": "core:custom-domain:create" + }, + (v1/*: any*/) + ], + "kind": "ScalarField", + "name": "permission", + "storageKey": null +}, +v3 = { + "alias": "canDeleteCustomDomain", + "args": [ + { + "kind": "Literal", + "name": "action", + "value": "core:custom-domain:delete" + }, + (v1/*: any*/) + ], + "kind": "ScalarField", + "name": "permission", + "storageKey": null +}, +v4 = [ + (v1/*: any*/) +], +v5 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null +}, +v6 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "role", + "storageKey": null +}, +v7 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null +}; +return { + "fragment": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Fragment", + "metadata": null, + "name": "domainSettingsPermissionsQuery", + "selections": [ + { + "kind": "RequiredField", + "field": { + "alias": null, + "args": null, + "concreteType": "Identity", + "kind": "LinkedField", + "name": "viewer", + "plural": false, + "selections": [ + (v2/*: any*/), + (v3/*: any*/) + ], + "storageKey": null + }, + "action": "THROW" + }, + { + "kind": "RequiredField", + "field": { + "alias": "organization", + "args": (v4/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v5/*: any*/), + { + "kind": "InlineFragment", + "selections": [ + { + "kind": "RequiredField", + "field": { + "alias": null, + "args": null, + "concreteType": "Membership", + "kind": "LinkedField", + "name": "viewerMembership", + "plural": false, + "selections": [ + (v6/*: any*/) + ], + "storageKey": null + }, + "action": "THROW" + } + ], + "type": "Organization", + "abstractKey": null + } + ], + "storageKey": null + }, + "action": "THROW" + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Operation", + "name": "domainSettingsPermissionsQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "Identity", + "kind": "LinkedField", + "name": "viewer", + "plural": false, + "selections": [ + (v2/*: any*/), + (v3/*: any*/), + (v7/*: any*/) + ], + "storageKey": null + }, + { + "alias": "organization", + "args": (v4/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v5/*: any*/), + { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "Membership", + "kind": "LinkedField", + "name": "viewerMembership", + "plural": false, + "selections": [ + (v6/*: any*/), + (v7/*: any*/) + ], + "storageKey": null + } + ], + "type": "Organization", + "abstractKey": null + }, + (v7/*: any*/) + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "de4faa8385c652071c57b4ddcc58e793", + "id": null, + "metadata": {}, + "name": "domainSettingsPermissionsQuery", + "operationKind": "query", + "text": "query domainSettingsPermissionsQuery(\n $organizationId: ID!\n) {\n viewer {\n canCreateCustomDomain: permission(action: \"core:custom-domain:create\", id: $organizationId)\n canDeleteCustomDomain: permission(action: \"core:custom-domain:delete\", id: $organizationId)\n id\n }\n organization: node(id: $organizationId) {\n __typename\n ... on Organization {\n viewerMembership {\n role\n id\n }\n }\n id\n }\n}\n" + } +}; +})(); + +(node as any).hash = "75397f9db7cb6f2320ff91f30e773e22"; + +export default node; diff --git a/apps/console/src/pages/organizations/settings/DomainSettingsPage.tsx b/apps/console/src/pages/organizations/settings/DomainSettingsPage.tsx new file mode 100644 index 000000000..c733129da --- /dev/null +++ b/apps/console/src/pages/organizations/settings/DomainSettingsPage.tsx @@ -0,0 +1,67 @@ +import { usePreloadedQuery, type PreloadedQuery } from "react-relay"; +import { graphql } from "relay-runtime"; +import type { DomainSettingsPageQuery } from "./__generated__/DomainSettingsPageQuery.graphql"; +import { useTranslate } from "@probo/i18n"; +import { DomainCard } from "./_components/DomainCard"; +import { NewDomainDialog } from "./_components/NewDomainDialog"; +import { Button, Card, IconPlusLarge } from "@probo/ui"; + +export const domainSettingsPageQuery = graphql` + query DomainSettingsPageQuery($organizationId: ID!) { + organization: node(id: $organizationId) { + __typename + ... on Organization { + id + customDomain { + domain + ...DomainCardFragment + } + } + } + } +`; + +export function DomainSettingsPage(props: { + queryRef: PreloadedQuery; +}) { + const { queryRef } = props; + + const { __ } = useTranslate(); + + const { organization } = usePreloadedQuery( + domainSettingsPageQuery, + queryRef, + ); + if (organization.__typename !== "Organization") { + throw new Error("invalid type for node"); + } + + return ( +
+

{__("Custom Domain")}

+ {organization.customDomain ? ( + + ) : ( + +
+

+ {__("No custom domain configured")} +

+

+ {__( + "Add your own domain to make your trust center more professional", + )} +

+
+ {/* {isAuthorized("Organization", "createCustomDomain") && ( */} + + + + {/* )} */} +
+
+
+ )} +
+ ); +} diff --git a/apps/console/src/pages/organizations/settings/DomainSettingsPageLoader.tsx b/apps/console/src/pages/organizations/settings/DomainSettingsPageLoader.tsx new file mode 100644 index 000000000..6850d139c --- /dev/null +++ b/apps/console/src/pages/organizations/settings/DomainSettingsPageLoader.tsx @@ -0,0 +1,38 @@ +import { Suspense, useEffect } from "react"; +import { useOrganizationId } from "/hooks/useOrganizationId"; +import { + DomainSettingsPage, + domainSettingsPageQuery, +} from "./DomainSettingsPage"; +import { useQueryLoader } from "react-relay"; +import { CoreRelayProvider } from "/providers/CoreRelayProvider"; +import type { DomainSettingsPageQuery } from "./__generated__/DomainSettingsPageQuery.graphql"; + +function DomainSettingsPageLoader() { + const organizationId = useOrganizationId(); + const [queryRef, loadQuery] = useQueryLoader( + domainSettingsPageQuery, + ); + + useEffect(() => { + loadQuery({ + organizationId, + }); + }, [loadQuery, organizationId]); + + if (!queryRef) { + return null; + } + + return ; +} + +export default function () { + return ( + + + + + + ); +} diff --git a/apps/console/src/pages/organizations/settings/DomainSettingsTab.tsx b/apps/console/src/pages/organizations/settings/DomainSettingsTab.tsx deleted file mode 100644 index 84dd01917..000000000 --- a/apps/console/src/pages/organizations/settings/DomainSettingsTab.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { useOutletContext } from "react-router"; -import { useFragment, graphql } from "react-relay"; -import { useTranslate } from "@probo/i18n"; -import { CustomDomainManager } from "/components/customDomains/CustomDomainManager"; -import type { DomainSettingsTabFragment$key } from "./__generated__/DomainSettingsTabFragment.graphql"; - -const domainSettingsTabFragment = graphql` - fragment DomainSettingsTabFragment on Organization { - id - customDomain { - id - domain - sslStatus - dnsRecords { - type - name - value - ttl - purpose - } - createdAt - updatedAt - sslExpiresAt - } - } -`; - -type OutletContext = { - organization: DomainSettingsTabFragment$key; -}; - -export default function DomainSettingsTab() { - const { __ } = useTranslate(); - const { organization: organizationKey } = useOutletContext(); - const organization = useFragment(domainSettingsTabFragment, organizationKey); - - return ( -
-

{__("Custom Domain")}

- -
- ); -} diff --git a/apps/console/src/pages/organizations/settings/__generated__/DomainSettingsPageQuery.graphql.ts b/apps/console/src/pages/organizations/settings/__generated__/DomainSettingsPageQuery.graphql.ts new file mode 100644 index 000000000..1a33d2bec --- /dev/null +++ b/apps/console/src/pages/organizations/settings/__generated__/DomainSettingsPageQuery.graphql.ts @@ -0,0 +1,247 @@ +/** + * @generated SignedSource<<04a0aa6648637fd8e81b371ffd8ef9fb>> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ConcreteRequest } from 'relay-runtime'; +import { FragmentRefs } from "relay-runtime"; +export type DomainSettingsPageQuery$variables = { + organizationId: string; +}; +export type DomainSettingsPageQuery$data = { + readonly organization: { + readonly __typename: "Organization"; + readonly customDomain: { + readonly domain: string; + readonly " $fragmentSpreads": FragmentRefs<"DomainCardFragment">; + } | null | undefined; + readonly id: string; + } | { + // This will never be '%other', but we need some + // value in case none of the concrete values match. + readonly __typename: "%other"; + }; +}; +export type DomainSettingsPageQuery = { + response: DomainSettingsPageQuery$data; + variables: DomainSettingsPageQuery$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": "__typename", + "storageKey": null +}, +v3 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null +}, +v4 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "domain", + "storageKey": null +}; +return { + "fragment": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Fragment", + "metadata": null, + "name": "DomainSettingsPageQuery", + "selections": [ + { + "alias": "organization", + "args": (v1/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v2/*: any*/), + { + "kind": "InlineFragment", + "selections": [ + (v3/*: any*/), + { + "alias": null, + "args": null, + "concreteType": "CustomDomain", + "kind": "LinkedField", + "name": "customDomain", + "plural": false, + "selections": [ + (v4/*: any*/), + { + "args": null, + "kind": "FragmentSpread", + "name": "DomainCardFragment" + } + ], + "storageKey": null + } + ], + "type": "Organization", + "abstractKey": null + } + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Operation", + "name": "DomainSettingsPageQuery", + "selections": [ + { + "alias": "organization", + "args": (v1/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v2/*: any*/), + (v3/*: any*/), + { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "CustomDomain", + "kind": "LinkedField", + "name": "customDomain", + "plural": false, + "selections": [ + (v4/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "sslStatus", + "storageKey": null + }, + { + "alias": null, + "args": null, + "concreteType": "DNSRecordInstruction", + "kind": "LinkedField", + "name": "dnsRecords", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "type", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "value", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "ttl", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "purpose", + "storageKey": null + } + ], + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "createdAt", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "updatedAt", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "sslExpiresAt", + "storageKey": null + }, + (v3/*: any*/) + ], + "storageKey": null + } + ], + "type": "Organization", + "abstractKey": null + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "561dce9d969f951c2c5b0a2a5fecf29a", + "id": null, + "metadata": {}, + "name": "DomainSettingsPageQuery", + "operationKind": "query", + "text": "query DomainSettingsPageQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n ... on Organization {\n id\n customDomain {\n domain\n ...DomainCardFragment\n id\n }\n }\n id\n }\n}\n\nfragment DomainCardFragment on CustomDomain {\n domain\n sslStatus\n ...DomainDialogFragment\n}\n\nfragment DomainDialogFragment on CustomDomain {\n sslStatus\n domain\n dnsRecords {\n type\n name\n value\n ttl\n purpose\n }\n createdAt\n updatedAt\n sslExpiresAt\n}\n" + } +}; +})(); + +(node as any).hash = "6b7bab65eca55ec2f132cd5944fe874e"; + +export default node; diff --git a/apps/console/src/pages/organizations/settings/__generated__/DomainSettingsTabFragment.graphql.ts b/apps/console/src/pages/organizations/settings/__generated__/DomainSettingsTabFragment.graphql.ts deleted file mode 100644 index ed5d40158..000000000 --- a/apps/console/src/pages/organizations/settings/__generated__/DomainSettingsTabFragment.graphql.ts +++ /dev/null @@ -1,154 +0,0 @@ -/** - * @generated SignedSource<<0e2aa976b1c9bb8dddf8b1dcc2f171d1>> - * @lightSyntaxTransform - * @nogrep - */ - -/* tslint:disable */ -/* eslint-disable */ -// @ts-nocheck - -import { ReaderFragment } from 'relay-runtime'; -export type SSLStatus = "ACTIVE" | "EXPIRED" | "FAILED" | "PENDING" | "PROVISIONING" | "RENEWING"; -import { FragmentRefs } from "relay-runtime"; -export type DomainSettingsTabFragment$data = { - readonly customDomain: { - readonly createdAt: any; - readonly dnsRecords: ReadonlyArray<{ - readonly name: string; - readonly purpose: string; - readonly ttl: number; - readonly type: string; - readonly value: string; - }>; - readonly domain: string; - readonly id: string; - readonly sslExpiresAt: any | null | undefined; - readonly sslStatus: SSLStatus; - readonly updatedAt: any; - } | null | undefined; - readonly id: string; - readonly " $fragmentType": "DomainSettingsTabFragment"; -}; -export type DomainSettingsTabFragment$key = { - readonly " $data"?: DomainSettingsTabFragment$data; - readonly " $fragmentSpreads": FragmentRefs<"DomainSettingsTabFragment">; -}; - -const node: ReaderFragment = (function(){ -var v0 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "id", - "storageKey": null -}; -return { - "argumentDefinitions": [], - "kind": "Fragment", - "metadata": null, - "name": "DomainSettingsTabFragment", - "selections": [ - (v0/*: any*/), - { - "alias": null, - "args": null, - "concreteType": "CustomDomain", - "kind": "LinkedField", - "name": "customDomain", - "plural": false, - "selections": [ - (v0/*: any*/), - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "domain", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "sslStatus", - "storageKey": null - }, - { - "alias": null, - "args": null, - "concreteType": "DNSRecordInstruction", - "kind": "LinkedField", - "name": "dnsRecords", - "plural": true, - "selections": [ - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "type", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "name", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "value", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "ttl", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "purpose", - "storageKey": null - } - ], - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "createdAt", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "updatedAt", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "sslExpiresAt", - "storageKey": null - } - ], - "storageKey": null - } - ], - "type": "Organization", - "abstractKey": null -}; -})(); - -(node as any).hash = "00306efb96d302284155f5324ba2fb99"; - -export default node; diff --git a/apps/console/src/pages/organizations/settings/_components/DeleteDomainDialog.tsx b/apps/console/src/pages/organizations/settings/_components/DeleteDomainDialog.tsx new file mode 100644 index 000000000..00b168050 --- /dev/null +++ b/apps/console/src/pages/organizations/settings/_components/DeleteDomainDialog.tsx @@ -0,0 +1,109 @@ +import { sprintf } from "@probo/helpers"; +import { useTranslate } from "@probo/i18n"; +import { + Button, + Dialog, + DialogContent, + DialogFooter, + Field, + IconTrashCan, + useDialogRef, +} from "@probo/ui"; +import { useState, type PropsWithChildren } from "react"; +import { graphql } from "relay-runtime"; +import { useMutationWithToasts } from "/hooks/useMutationWithToasts"; +import { useOrganizationId } from "/hooks/useOrganizationId"; +import type { DeleteDomainDialogMutation } from "./__generated__/DeleteDomainDialogMutation.graphql"; + +const deleteCustomDomainMutation = graphql` + mutation DeleteDomainDialogMutation($input: DeleteCustomDomainInput!) { + deleteCustomDomain(input: $input) { + deletedCustomDomainId + } + } +`; + +type DeleteDomainDialogProps = PropsWithChildren<{ + domain: string; +}>; + +export function DeleteDomainDialog(props: DeleteDomainDialogProps) { + const { children, domain } = props; + + const organizationId = useOrganizationId(); + const { __ } = useTranslate(); + const dialogRef = useDialogRef(); + const [inputValue, setInputValue] = useState(""); + + const [deleteCustomDomain, isDeleting] = + useMutationWithToasts( + deleteCustomDomainMutation, + { + successMessage: __("Domain deleted successfully"), + errorMessage: __("Failed to delete domain"), + }, + ); + + const handleDeleteDomain = async () => { + return deleteCustomDomain({ + variables: { + input: { organizationId }, + }, + onCompleted: () => { + dialogRef.current?.close(); + }, + updater: (store) => { + // Update the cache by setting customDomain to null + const organizationRecord = store.get(organizationId); + if (organizationRecord) { + organizationRecord.setValue(null, "customDomain"); + } + }, + }); + }; + + return ( + + +

+ {sprintf( + __( + "This will permanently delete the custom domain %s and all its configuration.", + ), + domain, + )} +

+ +

+ {__("This action cannot be undone.")} +

+ + setInputValue(e.target.value)} + placeholder={domain} + disabled={isDeleting} + autoComplete="off" + autoFocus + /> +
+ + + +
+ ); +} diff --git a/apps/console/src/pages/organizations/settings/_components/DomainCard.tsx b/apps/console/src/pages/organizations/settings/_components/DomainCard.tsx new file mode 100644 index 000000000..5e422b303 --- /dev/null +++ b/apps/console/src/pages/organizations/settings/_components/DomainCard.tsx @@ -0,0 +1,63 @@ +import { Badge, Button, Card } from "@probo/ui"; +import { DomainDialog } from "./DomainDialog"; +import { + getCustomDomainStatusBadgeLabel, + getCustomDomainStatusBadgeVariant, +} from "@probo/helpers"; +import { useTranslate } from "@probo/i18n"; +import { graphql } from "relay-runtime"; +import { useFragment } from "react-relay"; +import type { DomainCardFragment$key } from "./__generated__/DomainCardFragment.graphql"; +import { DeleteDomainDialog } from "./DeleteDomainDialog"; + +const fragment = graphql` + fragment DomainCardFragment on CustomDomain { + domain + sslStatus + ...DomainDialogFragment + } +`; + +export function DomainCard(props: { fKey: DomainCardFragment$key }) { + const { fKey } = props; + + const { __ } = useTranslate(); + + const domain = useFragment(fragment, fKey); + + return ( + +
+
+
+
+
{domain.domain}
+
+ {domain.sslStatus === "ACTIVE" + ? __("Verified") + : __("Pending verification")} +
+
+ + {getCustomDomainStatusBadgeLabel(domain.sslStatus, __)} + +
+ +
+ + + + + {/* {permissions.canDeleteCustomDomain && ( */} + + + + {/* )} */} +
+
+
+
+ ); +} diff --git a/apps/console/src/components/customDomains/DomainDetailsDialog.tsx b/apps/console/src/pages/organizations/settings/_components/DomainDialog.tsx similarity index 76% rename from apps/console/src/components/customDomains/DomainDetailsDialog.tsx rename to apps/console/src/pages/organizations/settings/_components/DomainDialog.tsx index 06f7c3bd0..a5abc0672 100644 --- a/apps/console/src/components/customDomains/DomainDetailsDialog.tsx +++ b/apps/console/src/pages/organizations/settings/_components/DomainDialog.tsx @@ -1,62 +1,46 @@ +import { + getCustomDomainStatusBadgeLabel, + getCustomDomainStatusBadgeVariant, +} from "@probo/helpers"; import { useTranslate } from "@probo/i18n"; import { + Badge, Button, Dialog, DialogContent, - Badge, useDialogRef, useToast, } from "@probo/ui"; -import type { ReactNode } from "react"; +import type { PropsWithChildren } from "react"; +import { useFragment } from "react-relay"; +import { graphql } from "relay-runtime"; +import type { DomainDialogFragment$key } from "./__generated__/DomainDialogFragment.graphql"; -interface DomainDetailsDialogProps { - children: ReactNode; - domain: { - readonly id: string; - readonly domain: string; - readonly sslStatus: string; - readonly dnsRecords?: - | readonly { - readonly type: string; - readonly name: string; - readonly value: string; - readonly ttl?: number; - readonly purpose: string; - }[] - | null; - readonly sslExpiresAt?: string | null; - }; -} +const fragment = graphql` + fragment DomainDialogFragment on CustomDomain { + sslStatus + domain + dnsRecords { + type + name + value + ttl + purpose + } + createdAt + updatedAt + sslExpiresAt + } +`; + +type DomainDialogProps = PropsWithChildren<{ fKey: DomainDialogFragment$key }>; + +export function DomainDialog(props: DomainDialogProps) { + const { children, fKey } = props; -export function DomainDetailsDialog({ - children, - domain, -}: DomainDetailsDialogProps) { const { __ } = useTranslate(); - const { toast } = useToast(); const dialogRef = useDialogRef(); - - const getStatusBadge = (domain: DomainDetailsDialogProps["domain"]) => { - if (domain.sslStatus === "ACTIVE") { - return {__("Active")}; - } - if ( - domain.sslStatus === "PROVISIONING" || - domain.sslStatus === "RENEWING" - ) { - return {__("Provisioning")}; - } - if (domain.sslStatus === "PENDING") { - return {__("Pending")}; - } - if (domain.sslStatus === "FAILED") { - return {__("Failed")}; - } - if (domain.sslStatus === "EXPIRED") { - return {__("Expired")}; - } - return {__("Unknown")}; - }; + const { toast } = useToast(); const copyToClipboard = (text: string) => { navigator.clipboard.writeText(text); @@ -67,6 +51,8 @@ export function DomainDetailsDialog({ }); }; + const domain = useFragment(fragment, fKey); + return ( {domain.domain} - {getStatusBadge(domain)} + + {getCustomDomainStatusBadgeLabel(domain.sslStatus, __)} + } > @@ -83,7 +71,7 @@ export function DomainDetailsDialog({
{__("Domain is active")}

{__( - "Your custom domain is verified and SSL certificate is active" + "Your custom domain is verified and SSL certificate is active", )}

{domain.sslExpiresAt && ( @@ -114,7 +102,7 @@ export function DomainDetailsDialog({

{__("DNS Configuration")}

{__( - "Add these DNS records to your domain to complete verification" + "Add these DNS records to your domain to complete verification", )}

@@ -172,7 +160,7 @@ export function DomainDetailsDialog({

{__( - "After adding the DNS records, verification will happen automatically. This may take a few minutes to propagate." + "After adding the DNS records, verification will happen automatically. This may take a few minutes to propagate.", )}

diff --git a/apps/console/src/components/customDomains/CreateCustomDomainDialog.tsx b/apps/console/src/pages/organizations/settings/_components/NewDomainDialog.tsx similarity index 76% rename from apps/console/src/components/customDomains/CreateCustomDomainDialog.tsx rename to apps/console/src/pages/organizations/settings/_components/NewDomainDialog.tsx index 14a97cb90..cd91a4f82 100644 --- a/apps/console/src/components/customDomains/CreateCustomDomainDialog.tsx +++ b/apps/console/src/pages/organizations/settings/_components/NewDomainDialog.tsx @@ -1,22 +1,23 @@ import { useTranslate } from "@probo/i18n"; -import { graphql } from "react-relay"; import { + Breadcrumb, Button, Dialog, DialogContent, DialogFooter, Field, useDialogRef, - Breadcrumb, } from "@probo/ui"; -import { z } from "zod"; +import { graphql } from "relay-runtime"; +import z from "zod"; import { useFormWithSchema } from "/hooks/useFormWithSchema"; import { useMutationWithToasts } from "/hooks/useMutationWithToasts"; -import type { ReactNode } from "react"; -import type { CreateCustomDomainDialogMutation } from "./__generated__/CreateCustomDomainDialogMutation.graphql"; +import { useOrganizationId } from "/hooks/useOrganizationId"; +import type { PropsWithChildren } from "react"; +import type { NewDomainDialogMutation } from "./__generated__/NewDomainDialogMutation.graphql"; const createCustomDomainMutation = graphql` - mutation CreateCustomDomainDialogMutation($input: CreateCustomDomainInput!) { + mutation NewDomainDialogMutation($input: CreateCustomDomainInput!) { createCustomDomain(input: $input) { customDomain { id @@ -43,21 +44,16 @@ const schema = z.object({ .min(1, "Domain is required") .regex( /^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$/i, - "Please enter a valid domain (e.g., compliance.example.com)" + "Please enter a valid domain (e.g., compliance.example.com)", ), }); -type FormData = z.infer; +type CustomDomainFormData = z.infer; -interface CreateCustomDomainDialogProps { - children: ReactNode; - organizationId: string; -} +export function NewDomainDialog(props: PropsWithChildren) { + const { children } = props; -export function CreateCustomDomainDialog({ - children, - organizationId, -}: CreateCustomDomainDialogProps) { + const organizationId = useOrganizationId(); const { __ } = useTranslate(); const dialogRef = useDialogRef(); @@ -67,21 +63,18 @@ export function CreateCustomDomainDialog({ defaultValues: { domain: "", }, - } + }, ); const [createCustomDomain, isCreating] = - useMutationWithToasts( - createCustomDomainMutation, - { - successMessage: __( - "Domain added successfully. Configure the DNS records to verify and activate your domain." - ), - errorMessage: __("Failed to add domain"), - } - ); + useMutationWithToasts(createCustomDomainMutation, { + successMessage: __( + "Domain added successfully. Configure the DNS records to verify and activate your domain.", + ), + errorMessage: __("Failed to add domain"), + }); - const onSubmit = handleSubmit(async (data: FormData) => { + const onSubmit = handleSubmit(async (data: CustomDomainFormData) => { const normalizedDomain = data.domain .trim() .toLowerCase() @@ -100,12 +93,12 @@ export function CreateCustomDomainDialog({ const organizationRecord = store.get(organizationId); if (organizationRecord && data?.createCustomDomain?.customDomain) { const customDomainRecord = store.get( - data.createCustomDomain.customDomain.id + data.createCustomDomain.customDomain.id, ); if (customDomainRecord) { organizationRecord.setLinkedRecord( customDomainRecord, - "customDomain" + "customDomain", ); } } @@ -128,7 +121,7 @@ export function CreateCustomDomainDialog({

{__( - "Enter your domain and we'll generate the DNS records you need to add" + "Enter your domain and we'll generate the DNS records you need to add", )}

diff --git a/apps/console/src/components/customDomains/__generated__/CustomDomainManagerDeleteMutation.graphql.ts b/apps/console/src/pages/organizations/settings/_components/__generated__/DeleteDomainDialogMutation.graphql.ts similarity index 65% rename from apps/console/src/components/customDomains/__generated__/CustomDomainManagerDeleteMutation.graphql.ts rename to apps/console/src/pages/organizations/settings/_components/__generated__/DeleteDomainDialogMutation.graphql.ts index 6a79b083a..4309d09f2 100644 --- a/apps/console/src/components/customDomains/__generated__/CustomDomainManagerDeleteMutation.graphql.ts +++ b/apps/console/src/pages/organizations/settings/_components/__generated__/DeleteDomainDialogMutation.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<> + * @generated SignedSource<<4f27f16c09a86fda0f17b4d33866fcb2>> * @lightSyntaxTransform * @nogrep */ @@ -12,17 +12,17 @@ import { ConcreteRequest } from 'relay-runtime'; export type DeleteCustomDomainInput = { organizationId: string; }; -export type CustomDomainManagerDeleteMutation$variables = { +export type DeleteDomainDialogMutation$variables = { input: DeleteCustomDomainInput; }; -export type CustomDomainManagerDeleteMutation$data = { +export type DeleteDomainDialogMutation$data = { readonly deleteCustomDomain: { readonly deletedCustomDomainId: string; }; }; -export type CustomDomainManagerDeleteMutation = { - response: CustomDomainManagerDeleteMutation$data; - variables: CustomDomainManagerDeleteMutation$variables; +export type DeleteDomainDialogMutation = { + response: DeleteDomainDialogMutation$data; + variables: DeleteDomainDialogMutation$variables; }; const node: ConcreteRequest = (function(){ @@ -64,7 +64,7 @@ return { "argumentDefinitions": (v0/*: any*/), "kind": "Fragment", "metadata": null, - "name": "CustomDomainManagerDeleteMutation", + "name": "DeleteDomainDialogMutation", "selections": (v1/*: any*/), "type": "Mutation", "abstractKey": null @@ -73,20 +73,20 @@ return { "operation": { "argumentDefinitions": (v0/*: any*/), "kind": "Operation", - "name": "CustomDomainManagerDeleteMutation", + "name": "DeleteDomainDialogMutation", "selections": (v1/*: any*/) }, "params": { - "cacheID": "9f4a3ed02de61bc60a8370e3771ca7bd", + "cacheID": "68e7fb0109af8d99c9947e5ef2314ad2", "id": null, "metadata": {}, - "name": "CustomDomainManagerDeleteMutation", + "name": "DeleteDomainDialogMutation", "operationKind": "mutation", - "text": "mutation CustomDomainManagerDeleteMutation(\n $input: DeleteCustomDomainInput!\n) {\n deleteCustomDomain(input: $input) {\n deletedCustomDomainId\n }\n}\n" + "text": "mutation DeleteDomainDialogMutation(\n $input: DeleteCustomDomainInput!\n) {\n deleteCustomDomain(input: $input) {\n deletedCustomDomainId\n }\n}\n" } }; })(); -(node as any).hash = "e3878d11e361c3da2471363664150f3d"; +(node as any).hash = "4ff39445b000bd5416e04e1460f11571"; export default node; diff --git a/apps/console/src/pages/organizations/settings/_components/__generated__/DomainCardFragment.graphql.ts b/apps/console/src/pages/organizations/settings/_components/__generated__/DomainCardFragment.graphql.ts new file mode 100644 index 000000000..5cddb7f97 --- /dev/null +++ b/apps/console/src/pages/organizations/settings/_components/__generated__/DomainCardFragment.graphql.ts @@ -0,0 +1,57 @@ +/** + * @generated SignedSource<> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ReaderFragment } from 'relay-runtime'; +export type SSLStatus = "ACTIVE" | "EXPIRED" | "FAILED" | "PENDING" | "PROVISIONING" | "RENEWING"; +import { FragmentRefs } from "relay-runtime"; +export type DomainCardFragment$data = { + readonly domain: string; + readonly sslStatus: SSLStatus; + readonly " $fragmentSpreads": FragmentRefs<"DomainDialogFragment">; + readonly " $fragmentType": "DomainCardFragment"; +}; +export type DomainCardFragment$key = { + readonly " $data"?: DomainCardFragment$data; + readonly " $fragmentSpreads": FragmentRefs<"DomainCardFragment">; +}; + +const node: ReaderFragment = { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "DomainCardFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "domain", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "sslStatus", + "storageKey": null + }, + { + "args": null, + "kind": "FragmentSpread", + "name": "DomainDialogFragment" + } + ], + "type": "CustomDomain", + "abstractKey": null +}; + +(node as any).hash = "672098f5351cf031d958263155e180ac"; + +export default node; diff --git a/apps/console/src/pages/organizations/settings/_components/__generated__/DomainDialogFragment.graphql.ts b/apps/console/src/pages/organizations/settings/_components/__generated__/DomainDialogFragment.graphql.ts new file mode 100644 index 000000000..e278cccc6 --- /dev/null +++ b/apps/console/src/pages/organizations/settings/_components/__generated__/DomainDialogFragment.graphql.ts @@ -0,0 +1,128 @@ +/** + * @generated SignedSource<<1ee5bd8001e3d48b51c2869cd1ff5771>> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ReaderFragment } from 'relay-runtime'; +export type SSLStatus = "ACTIVE" | "EXPIRED" | "FAILED" | "PENDING" | "PROVISIONING" | "RENEWING"; +import { FragmentRefs } from "relay-runtime"; +export type DomainDialogFragment$data = { + readonly createdAt: any; + readonly dnsRecords: ReadonlyArray<{ + readonly name: string; + readonly purpose: string; + readonly ttl: number; + readonly type: string; + readonly value: string; + }>; + readonly domain: string; + readonly sslExpiresAt: any | null | undefined; + readonly sslStatus: SSLStatus; + readonly updatedAt: any; + readonly " $fragmentType": "DomainDialogFragment"; +}; +export type DomainDialogFragment$key = { + readonly " $data"?: DomainDialogFragment$data; + readonly " $fragmentSpreads": FragmentRefs<"DomainDialogFragment">; +}; + +const node: ReaderFragment = { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "DomainDialogFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "sslStatus", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "domain", + "storageKey": null + }, + { + "alias": null, + "args": null, + "concreteType": "DNSRecordInstruction", + "kind": "LinkedField", + "name": "dnsRecords", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "type", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "value", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "ttl", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "purpose", + "storageKey": null + } + ], + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "createdAt", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "updatedAt", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "sslExpiresAt", + "storageKey": null + } + ], + "type": "CustomDomain", + "abstractKey": null +}; + +(node as any).hash = "0aec5615023dde2d23901fec9e43ed9b"; + +export default node; diff --git a/apps/console/src/components/customDomains/__generated__/CreateCustomDomainDialogMutation.graphql.ts b/apps/console/src/pages/organizations/settings/_components/__generated__/NewDomainDialogMutation.graphql.ts similarity index 82% rename from apps/console/src/components/customDomains/__generated__/CreateCustomDomainDialogMutation.graphql.ts rename to apps/console/src/pages/organizations/settings/_components/__generated__/NewDomainDialogMutation.graphql.ts index 07894e530..c0eb48379 100644 --- a/apps/console/src/components/customDomains/__generated__/CreateCustomDomainDialogMutation.graphql.ts +++ b/apps/console/src/pages/organizations/settings/_components/__generated__/NewDomainDialogMutation.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -14,10 +14,10 @@ export type CreateCustomDomainInput = { domain: string; organizationId: string; }; -export type CreateCustomDomainDialogMutation$variables = { +export type NewDomainDialogMutation$variables = { input: CreateCustomDomainInput; }; -export type CreateCustomDomainDialogMutation$data = { +export type NewDomainDialogMutation$data = { readonly createCustomDomain: { readonly customDomain: { readonly createdAt: any; @@ -36,9 +36,9 @@ export type CreateCustomDomainDialogMutation$data = { }; }; }; -export type CreateCustomDomainDialogMutation = { - response: CreateCustomDomainDialogMutation$data; - variables: CreateCustomDomainDialogMutation$variables; +export type NewDomainDialogMutation = { + response: NewDomainDialogMutation$data; + variables: NewDomainDialogMutation$variables; }; const node: ConcreteRequest = (function(){ @@ -172,7 +172,7 @@ return { "argumentDefinitions": (v0/*: any*/), "kind": "Fragment", "metadata": null, - "name": "CreateCustomDomainDialogMutation", + "name": "NewDomainDialogMutation", "selections": (v1/*: any*/), "type": "Mutation", "abstractKey": null @@ -181,20 +181,20 @@ return { "operation": { "argumentDefinitions": (v0/*: any*/), "kind": "Operation", - "name": "CreateCustomDomainDialogMutation", + "name": "NewDomainDialogMutation", "selections": (v1/*: any*/) }, "params": { - "cacheID": "5e5bf90d17b1dc5129d6a0fc5d68d46c", + "cacheID": "b8aa27fe9ab3442dea9b96a70966524b", "id": null, "metadata": {}, - "name": "CreateCustomDomainDialogMutation", + "name": "NewDomainDialogMutation", "operationKind": "mutation", - "text": "mutation CreateCustomDomainDialogMutation(\n $input: CreateCustomDomainInput!\n) {\n createCustomDomain(input: $input) {\n customDomain {\n id\n domain\n sslStatus\n dnsRecords {\n type\n name\n value\n ttl\n purpose\n }\n createdAt\n updatedAt\n sslExpiresAt\n }\n }\n}\n" + "text": "mutation NewDomainDialogMutation(\n $input: CreateCustomDomainInput!\n) {\n createCustomDomain(input: $input) {\n customDomain {\n id\n domain\n sslStatus\n dnsRecords {\n type\n name\n value\n ttl\n purpose\n }\n createdAt\n updatedAt\n sslExpiresAt\n }\n }\n}\n" } }; })(); -(node as any).hash = "fb3bede811b12db54f54af3dd375e335"; +(node as any).hash = "f49cd22c5d6656e662fad5afab6fc344"; export default node; diff --git a/apps/console/src/routes.tsx b/apps/console/src/routes.tsx index 2e09af6c1..d20c6ac45 100644 --- a/apps/console/src/routes.tsx +++ b/apps/console/src/routes.tsx @@ -119,9 +119,7 @@ const routes = [ }, { path: "documents/signing-requests", - Component: lazy( - () => import("./pages/DocumentSigningRequestsPage.tsx"), - ), + Component: lazy(() => import("./pages/DocumentSigningRequestsPage")), }, { path: "api-keys", @@ -170,7 +168,7 @@ const routes = [ { path: "/organizations/:organizationId", Component: lazy( - () => import("./pages/iam/memberships/MembershipLayoutLoader.tsx"), + () => import("./pages/iam/memberships/MembershipLayoutLoader"), ), ErrorBoundary: ErrorBoundary, children: [ @@ -192,7 +190,7 @@ const routes = [ path: "settings", Fallback: PageSkeleton, Component: lazy( - () => import("./pages/iam/organizations/settings/SettingsLayout.tsx"), + () => import("./pages/iam/organizations/settings/SettingsLayout"), ), children: [ { @@ -205,20 +203,21 @@ const routes = [ path: "general", Component: lazy( () => - import("./pages/iam/organizations/settings/GeneralSettingsPageLoader.tsx"), + import("./pages/iam/organizations/settings/GeneralSettingsPageLoader"), ), }, { path: "members", Component: lazy( () => - import("./pages/iam/organizations/settings/MembersPageLoader.tsx"), + import("./pages/iam/organizations/settings/MembersPageLoader"), ), }, { path: "domain", Component: lazy( - () => import("./pages/organizations/settings/DomainSettingsTab"), + () => + import("./pages/organizations/settings/DomainSettingsPageLoader"), ), }, { diff --git a/packages/helpers/src/customDomain.ts b/packages/helpers/src/customDomain.ts new file mode 100644 index 000000000..f94de92bc --- /dev/null +++ b/packages/helpers/src/customDomain.ts @@ -0,0 +1,45 @@ +type Status = + | "ACTIVE" + | "PROVISIONING" + | "RENEWING" + | "PENDING" + | "FAILED" + | "EXPIRED"; + +export const getCustomDomainStatusBadgeVariant = (sslStatus: Status) => { + switch (sslStatus) { + case "ACTIVE": + return "success" as const; + case "PROVISIONING": + case "RENEWING": + case "PENDING": + return "warning" as const; + case "FAILED": + case "EXPIRED": + return "danger" as const; + default: + return "neutral" as const; + } +}; + +export const getCustomDomainStatusBadgeLabel = ( + sslStatus: Status, + __: (key: string) => string, +) => { + if (sslStatus === "ACTIVE") { + return __("Active"); + } + if (sslStatus === "PROVISIONING" || sslStatus === "RENEWING") { + return __("Provisioning"); + } + if (sslStatus === "PENDING") { + return __("Pending"); + } + if (sslStatus === "FAILED") { + return __("Failed"); + } + if (sslStatus === "EXPIRED") { + return __("Expired"); + } + return __("Unknown"); +};