Rewrite identity and access management

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
Bryan Frimin
2025-12-03 19:23:15 +01:00
parent 4ed3f5a067
commit 74fc3b8cd1
201 changed files with 32895 additions and 23649 deletions

View File

@@ -18,6 +18,7 @@
"@probo/hooks": "1.0.0",
"@probo/i18n": "1.0.0",
"@probo/react-lazy": "1.0.0",
"@probo/relay": "^1.0.0",
"@probo/routes": "^1.0.0",
"@probo/ui": "1.0.0",
"@probo/vendors": "0.0.1",

View File

@@ -2,7 +2,7 @@ import { useLocation, useRouteError } from "react-router";
import { IconPageCross } from "@probo/ui";
import { useTranslate } from "@probo/i18n";
import { useEffect, useRef } from "react";
import { AuthenticationRequiredError } from "/providers/RelayProviders";
import { AuthenticationRequiredError } from "@probo/relay";
const classNames = {
wrapper: "py-10 text-center space-y-2 ",

View File

@@ -0,0 +1,18 @@
import { Environment, Network, RecordSource, Store } from "relay-runtime";
import { makeFetchQuery } from "@probo/relay";
const source = new RecordSource();
const store = new Store(source, {
queryCacheExpirationTime: 1 * 60 * 1000,
gcReleaseBufferSize: 20,
});
export const consoleEnvironment = new Environment({
network: Network.create(makeFetchQuery("/api/console/v1/query")),
store,
});
export const connectEnvironment = new Environment({
network: Network.create(makeFetchQuery("/api/connect/v1/query")),
store,
});

View File

@@ -32,7 +32,6 @@ import type { EmployeeLayoutQuery as EmployeeLayoutQueryType } from "./__generat
import { Suspense, useState, useEffect, useMemo, use } from "react";
import { ErrorBoundary } from "react-error-boundary";
import { PageError } from "/components/PageError";
import { buildEndpoint } from "/providers/RelayProviders";
import { PermissionsProvider } from "/providers/PermissionsProvider";
import { PermissionsContext } from "/providers/PermissionsContext";
@@ -71,11 +70,7 @@ export function EmployeeLayout() {
);
}
function EmployeeLayoutContent({
organizationId,
}: {
organizationId: string;
}) {
function EmployeeLayoutContent({ organizationId }: { organizationId: string }) {
const data = useLazyLoadQuery<EmployeeLayoutQueryType>(EmployeeLayoutQuery, {
organizationId,
});
@@ -352,7 +347,7 @@ function UserDropdown({ organizationId }: { organizationId: string }) {
) => {
e.preventDefault();
fetch(buildEndpoint("/connect/logout"), {
fetch("/connect/logout", {
method: "DELETE",
headers: {
"Content-Type": "application/json",

View File

@@ -50,7 +50,6 @@ import { graphql } from "relay-runtime";
import type { MainLayoutQuery as MainLayoutQueryType } from "./__generated__/MainLayoutQuery.graphql";
import { PageError } from "/components/PageError";
import { PermissionsProvider } from "/providers/PermissionsProvider";
import { buildEndpoint } from "/providers/RelayProviders";
import { PermissionsContext } from "/providers/PermissionsContext";
const MainLayoutQuery = graphql`
@@ -283,7 +282,7 @@ function UserDropdown({ organizationId }: { organizationId: string }) {
) => {
e.preventDefault();
fetch(buildEndpoint("/connect/logout"), {
fetch("/connect/logout", {
method: "DELETE",
headers: {
"Content-Type": "application/json",
@@ -371,7 +370,6 @@ interface InvitationsResponse {
invitations: Invitation[];
}
function OrganizationSelector({
currentOrganization,
}: {

View File

@@ -1,7 +1,6 @@
import { Outlet } from "react-router";
import { Logo, Button, IconArrowBoxLeft, useToast } from "@probo/ui";
import { useTranslate } from "@probo/i18n";
import { buildEndpoint } from "/providers/RelayProviders";
import type { ReactNode } from "react";
type Props = {
@@ -11,18 +10,23 @@ type Props = {
isAuthenticated?: boolean;
};
export function PublicTrustCenterLayout({ organizationName, organizationLogo, children, isAuthenticated }: Props) {
export function PublicTrustCenterLayout({
organizationName,
organizationLogo,
children,
isAuthenticated,
}: Props) {
const { __ } = useTranslate();
const { toast } = useToast();
const handleLogout = async () => {
const handleLogout = async () => {
try {
const response = await fetch(buildEndpoint('/api/trust/v1/auth/logout'), {
method: 'DELETE',
const response = await fetch("/api/trust/v1/auth/logout", {
method: "DELETE",
headers: {
'Content-Type': 'application/json',
"Content-Type": "application/json",
},
credentials: 'include',
credentials: "include",
});
if (!response.ok) {

View File

@@ -1,7 +1,6 @@
import { createRoot } from "react-dom/client";
import "./index.css";
import { App } from "./App";
import { RelayProvider } from "./providers/RelayProviders";
import { TranslatorProvider } from "./providers/TranslatorProvider";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
@@ -15,10 +14,8 @@ const queryClient = new QueryClient({
createRoot(document.getElementById("root")!).render(
<QueryClientProvider client={queryClient}>
<RelayProvider>
<TranslatorProvider>
<App />
</TranslatorProvider>
</RelayProvider>
</QueryClientProvider>,
<TranslatorProvider>
<App />
</TranslatorProvider>
</QueryClientProvider>
);

View File

@@ -31,7 +31,7 @@ import { formatDate } from "@probo/helpers";
import { useFormWithSchema } from "/hooks/useFormWithSchema";
import { Controller } from "react-hook-form";
import { z } from "zod";
import { UnAuthenticatedError } from "/providers/RelayProviders";
import { UnAuthenticatedError } from "@probo/relay";
interface APIKey {
id: string;
@@ -56,10 +56,14 @@ interface Organization {
const createSchema = z.object({
name: z.string().min(1, "Name is required"),
expiresIn: z.enum(["1month", "3months", "6months", "1year"]),
organizations: z.array(z.object({
organizationId: z.string(),
role: z.string(),
})).min(1, "At least one organization is required"),
organizations: z
.array(
z.object({
organizationId: z.string(),
role: z.string(),
})
)
.min(1, "At least one organization is required"),
});
type CreateFormData = z.infer<typeof createSchema>;
@@ -79,19 +83,24 @@ export default function APIKeysPage() {
const [isCreating, setIsCreating] = useState(false);
const [isUpdating, setIsUpdating] = useState(false);
const [isDeleting, setIsDeleting] = useState(false);
const [selectedOrganizations, setSelectedOrganizations] = useState<string[]>([]);
const [organizationRoles, setOrganizationRoles] = useState<Record<string, string>>({});
const [selectedOrganizations, setSelectedOrganizations] = useState<string[]>(
[]
);
const [organizationRoles, setOrganizationRoles] = useState<
Record<string, string>
>({});
const [editingAPIKey, setEditingAPIKey] = useState<APIKey | null>(null);
const [editingName, setEditingName] = useState<string>("");
const [error, setError] = useState<Error | null>(null);
const { formState, handleSubmit, reset, control, setValue, register } = useFormWithSchema(createSchema, {
defaultValues: {
name: new Date().toISOString().split('T')[0],
expiresIn: "1month",
organizations: [],
},
});
const { formState, handleSubmit, reset, control, setValue, register } =
useFormWithSchema(createSchema, {
defaultValues: {
name: new Date().toISOString().split("T")[0],
expiresIn: "1month",
organizations: [],
},
});
if (error) {
throw error;
@@ -99,14 +108,16 @@ export default function APIKeysPage() {
const fetchAPIKeys = async () => {
try {
const response = await fetch('/connect/api-keys', { credentials: 'include' });
const response = await fetch("/connect/api-keys", {
credentials: "include",
});
if (!response.ok) {
throw new Error('Failed to fetch API keys');
throw new Error("Failed to fetch API keys");
}
const data: { apiKeys: APIKey[] } = await response.json();
setApiKeys(data.apiKeys);
} catch (err) {
console.error('Failed to fetch API keys:', err);
console.error("Failed to fetch API keys:", err);
toast({
title: __("Error"),
description: __("Failed to load API keys"),
@@ -119,32 +130,40 @@ export default function APIKeysPage() {
const fetchData = async () => {
try {
const [apiKeysResponse, organizationsResponse] = await Promise.all([
fetch('/connect/api-keys', { credentials: 'include' }),
fetch('/connect/organizations?role=OWNER', { credentials: 'include' }),
fetch("/connect/api-keys", { credentials: "include" }),
fetch("/connect/organizations?role=OWNER", {
credentials: "include",
}),
]);
if (apiKeysResponse.status === 401 || organizationsResponse.status === 401) {
if (
apiKeysResponse.status === 401 ||
organizationsResponse.status === 401
) {
setError(new UnAuthenticatedError());
return;
}
if (!apiKeysResponse.ok) {
throw new Error('Failed to fetch API keys');
throw new Error("Failed to fetch API keys");
}
if (!organizationsResponse.ok) {
throw new Error('Failed to fetch organizations');
throw new Error("Failed to fetch organizations");
}
const apiKeysData: { apiKeys: APIKey[] } = await apiKeysResponse.json();
const orgsData: { organizations: Organization[] } = await organizationsResponse.json();
const orgsData: { organizations: Organization[] } =
await organizationsResponse.json();
const authenticatedOrgs = orgsData.organizations.filter(org => org.authStatus === "authenticated");
const authenticatedOrgs = orgsData.organizations.filter(
(org) => org.authStatus === "authenticated"
);
setApiKeys(apiKeysData.apiKeys);
setOrganizations(authenticatedOrgs);
} catch (err) {
console.error('Failed to fetch data:', err);
console.error("Failed to fetch data:", err);
toast({
title: __("Error"),
description: __("Failed to load data"),
@@ -179,12 +198,12 @@ export default function APIKeysPage() {
setIsCreating(true);
try {
const response = await fetch('/connect/api-keys', {
method: 'POST',
const response = await fetch("/connect/api-keys", {
method: "POST",
headers: {
'Content-Type': 'application/json',
"Content-Type": "application/json",
},
credentials: 'include',
credentials: "include",
body: JSON.stringify({
name: formData.name,
expiresAt: expiresAt.toISOString(),
@@ -193,7 +212,7 @@ export default function APIKeysPage() {
});
if (!response.ok) {
throw new Error('Failed to create API key');
throw new Error("Failed to create API key");
}
const data: { apiKey: APIKey; key: string } = await response.json();
@@ -224,9 +243,9 @@ export default function APIKeysPage() {
const handleEdit = (apiKey: APIKey) => {
setEditingAPIKey(apiKey);
setEditingName(apiKey.name);
const orgIds = apiKey.organizations.map(org => org.organizationId);
const orgIds = apiKey.organizations.map((org) => org.organizationId);
const roles: Record<string, string> = {};
apiKey.organizations.forEach(org => {
apiKey.organizations.forEach((org) => {
roles[org.organizationId] = org.role;
});
setSelectedOrganizations(orgIds);
@@ -239,16 +258,16 @@ export default function APIKeysPage() {
setIsUpdating(true);
try {
const response = await fetch('/connect/api-keys', {
method: 'PUT',
const response = await fetch("/connect/api-keys", {
method: "PUT",
headers: {
'Content-Type': 'application/json',
"Content-Type": "application/json",
},
credentials: 'include',
credentials: "include",
body: JSON.stringify({
id: editingAPIKey.id,
name: editingName,
organizations: selectedOrganizations.map(id => ({
organizations: selectedOrganizations.map((id) => ({
organizationId: id,
role: organizationRoles[id] || "FULL",
})),
@@ -256,7 +275,7 @@ export default function APIKeysPage() {
});
if (!response.ok) {
throw new Error('Failed to update API key');
throw new Error("Failed to update API key");
}
await fetchAPIKeys();
@@ -286,20 +305,20 @@ export default function APIKeysPage() {
async () => {
setIsDeleting(true);
try {
const response = await fetch('/connect/api-keys', {
method: 'DELETE',
const response = await fetch("/connect/api-keys", {
method: "DELETE",
headers: {
'Content-Type': 'application/json',
"Content-Type": "application/json",
},
credentials: 'include',
credentials: "include",
body: JSON.stringify({ id }),
});
if (!response.ok) {
throw new Error('Failed to delete API key');
throw new Error("Failed to delete API key");
}
setApiKeys(apiKeys.filter(key => key.id !== id));
setApiKeys(apiKeys.filter((key) => key.id !== id));
toast({
title: __("Success"),
description: __("API Key deleted successfully"),
@@ -317,7 +336,9 @@ export default function APIKeysPage() {
}
},
{
message: __(`Are you sure you want to delete the API key "${name}"? This action cannot be undone.`),
message: __(
`Are you sure you want to delete the API key "${name}"? This action cannot be undone.`
),
}
);
};
@@ -326,11 +347,11 @@ export default function APIKeysPage() {
setIsLoadingKey(true);
try {
const response = await fetch(`/connect/api-keys/${id}`, {
credentials: 'include',
credentials: "include",
});
if (!response.ok) {
throw new Error('Failed to load API key');
throw new Error("Failed to load API key");
}
const data: { key: string } = await response.json();
@@ -400,7 +421,7 @@ export default function APIKeysPage() {
return (
<Card key={apiKey.id} padded className="w-full">
<div className="flex items-start justify-between gap-4">
<div className="flex items-start justify-between gap-4">
<div className="flex-1 space-y-2">
<div className="flex items-center gap-2">
<h3 className="text-lg font-semibold">{apiKey.name}</h3>
@@ -464,7 +485,9 @@ export default function APIKeysPage() {
{__("Create an API key")}
</h2>
<p className="text-txt-tertiary mb-4">
{__("Generate a new API key for programmatic access to your organization")}
{__(
"Generate a new API key for programmatic access to your organization"
)}
</p>
<Button
onClick={() => dialogRef.current?.open()}
@@ -516,31 +539,34 @@ export default function APIKeysPage() {
type="button"
variant="tertiary"
onClick={() => {
const allSelected = selectedOrganizations.length === organizations.length;
const allSelected =
selectedOrganizations.length === organizations.length;
if (allSelected) {
setSelectedOrganizations([]);
setOrganizationRoles({});
setValue("organizations", []);
} else {
const allOrgIds = organizations.map(org => org.id);
const allOrgIds = organizations.map((org) => org.id);
const newRoles: Record<string, string> = {};
allOrgIds.forEach(id => {
allOrgIds.forEach((id) => {
newRoles[id] = organizationRoles[id] || "FULL";
});
setSelectedOrganizations(allOrgIds);
setOrganizationRoles(newRoles);
setValue(
"organizations",
allOrgIds.map(id => ({
allOrgIds.map((id) => ({
organizationId: id,
role: newRoles[id]
role: newRoles[id],
}))
);
}
}}
className="text-xs h-7 min-h-7"
>
{selectedOrganizations.length === organizations.length ? __("Clear All") : __("Select All")}
{selectedOrganizations.length === organizations.length
? __("Clear All")
: __("Select All")}
</Button>
)}
</div>
@@ -556,9 +582,7 @@ export default function APIKeysPage() {
<Th>{__("Name")}</Th>
<Th width={180}>{__("Role")}</Th>
<Th width={100}>
<div className="flex justify-end">
{__("Access")}
</div>
<div className="flex justify-end">{__("Access")}</div>
</Th>
</Tr>
</Thead>
@@ -570,30 +594,33 @@ export default function APIKeysPage() {
{org.name}
</div>
</Td>
<Td>
<div className="min-h-[36px] flex items-center">
{selectedOrganizations.includes(org.id) ? (
<Select
value={organizationRoles[org.id] || "FULL"}
onValueChange={(role) => {
const newRoles = { ...organizationRoles, [org.id]: role };
setOrganizationRoles(newRoles);
setValue(
"organizations",
selectedOrganizations.map(id => ({
organizationId: id,
role: newRoles[id] || "FULL"
}))
);
}}
>
<Option value="FULL">{__("Full")}</Option>
</Select>
) : (
<span className="text-txt-tertiary">—</span>
)}
</div>
</Td>
<Td>
<div className="min-h-[36px] flex items-center">
{selectedOrganizations.includes(org.id) ? (
<Select
value={organizationRoles[org.id] || "FULL"}
onValueChange={(role) => {
const newRoles = {
...organizationRoles,
[org.id]: role,
};
setOrganizationRoles(newRoles);
setValue(
"organizations",
selectedOrganizations.map((id) => ({
organizationId: id,
role: newRoles[id] || "FULL",
}))
);
}}
>
<Option value="FULL">{__("Full")}</Option>
</Select>
) : (
<span className="text-txt-tertiary">—</span>
)}
</div>
</Td>
<Td>
<div className="flex justify-end">
<Checkbox
@@ -602,21 +629,26 @@ export default function APIKeysPage() {
let newSelected: string[];
const newRoles = { ...organizationRoles };
if (checked) {
newSelected = [...selectedOrganizations, org.id];
newSelected = [
...selectedOrganizations,
org.id,
];
if (!newRoles[org.id]) {
newRoles[org.id] = "FULL";
}
} else {
newSelected = selectedOrganizations.filter(id => id !== org.id);
newSelected = selectedOrganizations.filter(
(id) => id !== org.id
);
delete newRoles[org.id];
}
setSelectedOrganizations(newSelected);
setOrganizationRoles(newRoles);
setValue(
"organizations",
newSelected.map(id => ({
newSelected.map((id) => ({
organizationId: id,
role: newRoles[id] || "FULL"
role: newRoles[id] || "FULL",
}))
);
}}
@@ -660,14 +692,15 @@ export default function APIKeysPage() {
type="button"
variant="tertiary"
onClick={() => {
const allSelected = selectedOrganizations.length === organizations.length;
const allSelected =
selectedOrganizations.length === organizations.length;
if (allSelected) {
setSelectedOrganizations([]);
setOrganizationRoles({});
} else {
const allOrgIds = organizations.map(org => org.id);
const allOrgIds = organizations.map((org) => org.id);
const newRoles: Record<string, string> = {};
allOrgIds.forEach(id => {
allOrgIds.forEach((id) => {
newRoles[id] = organizationRoles[id] || "FULL";
});
setSelectedOrganizations(allOrgIds);
@@ -676,7 +709,9 @@ export default function APIKeysPage() {
}}
className="text-xs h-7 min-h-7"
>
{selectedOrganizations.length === organizations.length ? __("Clear All") : __("Select All")}
{selectedOrganizations.length === organizations.length
? __("Clear All")
: __("Select All")}
</Button>
)}
</div>
@@ -692,9 +727,7 @@ export default function APIKeysPage() {
<Th>{__("Name")}</Th>
<Th width={180}>{__("Role")}</Th>
<Th width={100}>
<div className="flex justify-end">
{__("Access")}
</div>
<div className="flex justify-end">{__("Access")}</div>
</Th>
</Tr>
</Thead>
@@ -712,7 +745,10 @@ export default function APIKeysPage() {
<Select
value={organizationRoles[org.id] || "FULL"}
onValueChange={(role) => {
const newRoles = { ...organizationRoles, [org.id]: role };
const newRoles = {
...organizationRoles,
[org.id]: role,
};
setOrganizationRoles(newRoles);
}}
>
@@ -731,12 +767,17 @@ export default function APIKeysPage() {
let newSelected: string[];
const newRoles = { ...organizationRoles };
if (checked) {
newSelected = [...selectedOrganizations, org.id];
newSelected = [
...selectedOrganizations,
org.id,
];
if (!newRoles[org.id]) {
newRoles[org.id] = "FULL";
}
} else {
newSelected = selectedOrganizations.filter(id => id !== org.id);
newSelected = selectedOrganizations.filter(
(id) => id !== org.id
);
delete newRoles[org.id];
}
setSelectedOrganizations(newSelected);
@@ -754,7 +795,13 @@ export default function APIKeysPage() {
</div>
</DialogContent>
<DialogFooter>
<Button onClick={handleUpdate} disabled={isUpdating || (selectedOrganizations.length === 0 && !editingName.trim())}>
<Button
onClick={handleUpdate}
disabled={
isUpdating ||
(selectedOrganizations.length === 0 && !editingName.trim())
}
>
{isUpdating ? __("Updating...") : __("Update")}
</Button>
</DialogFooter>

View File

@@ -10,7 +10,6 @@ import {
IconCircleCheck,
} from "@probo/ui";
import { PDFPreview } from "../components/documents/PDFPreview";
import { buildEndpoint } from "/providers/RelayProviders";
import { useWindowSize } from "usehooks-ts";
import clsx from "clsx";
import { sprintf } from "@probo/helpers";
@@ -63,7 +62,7 @@ export default function DocumentSigningRequestsPage() {
async function fetchDocuments() {
try {
const response = await fetch(
buildEndpoint("/api/console/v1/documents/signing-requests"),
"/api/console/v1/documents/signing-requests",
{
method: "GET",
headers: {
@@ -114,9 +113,7 @@ export default function DocumentSigningRequestsPage() {
setSigning(true);
try {
const response = await fetch(
buildEndpoint(
`/api/console/v1/documents/signing-requests/${docToSign.document_version_id}/sign`
),
`/api/console/v1/documents/signing-requests/${docToSign.document_version_id}/sign`,
{
method: "POST",
headers: {
@@ -235,9 +232,7 @@ export default function DocumentSigningRequestsPage() {
// Build PDF URL with watermark
const pdfUrl = token
? `${buildEndpoint(
`/api/console/v1/documents/signing-requests/${currentDoc.document_version_id}/pdf`
)}?token=${encodeURIComponent(token)}`
? `${`/api/console/v1/documents/signing-requests/${currentDoc.document_version_id}/pdf`}?token=${encodeURIComponent(token)}`
: null;
return (
@@ -250,7 +245,10 @@ export default function DocumentSigningRequestsPage() {
<div className="grid lg:grid-cols-2 min-h-0 h-full">
<div className="max-w-[440px] mx-auto py-20 overflow-y-auto scrollbar-hide">
<h1 className="text-2xl font-semibold mb-6">
{sprintf(__("%s requests your signature"), signingData.organizationName)}
{sprintf(
__("%s requests your signature"),
signingData.organizationName
)}
</h1>
{allSigned ? (
<p className="text-txt-secondary text-base">
@@ -266,7 +264,10 @@ export default function DocumentSigningRequestsPage() {
<Card className="mb-6 overflow-hidden">
<div className="divide-y divide-border-solid">
{(() => {
const renderDocumentItem = (doc: Document, index: number) => (
const renderDocumentItem = (
doc: Document,
index: number
) => (
<div
key={doc.document_version_id}
className={clsx(
@@ -307,15 +308,15 @@ export default function DocumentSigningRequestsPage() {
doc.signed
? "bg-green-100 text-green-800"
: index === currentDocIndex
? "bg-blue-100 text-blue-800"
: "bg-gray-100 text-gray-700"
? "bg-blue-100 text-blue-800"
: "bg-gray-100 text-gray-700"
)}
>
{doc.signed
? __("Signed")
: index === currentDocIndex
? __("In review")
: __("Waiting signature")}
? __("In review")
: __("Waiting signature")}
</span>
</div>
</div>
@@ -352,8 +353,12 @@ export default function DocumentSigningRequestsPage() {
const currentIsLast = currentDocIndex === totalDocs - 1;
// Calculate hidden docs before and after current
const hiddenBeforeCurrent = currentIsFirst ? 0 : currentDocIndex - 1;
const hiddenAfterCurrent = currentIsLast ? 0 : totalDocs - currentDocIndex - 2;
const hiddenBeforeCurrent = currentIsFirst
? 0
: currentDocIndex - 1;
const hiddenAfterCurrent = currentIsLast
? 0
: totalDocs - currentDocIndex - 2;
return (
<>
@@ -367,12 +372,16 @@ export default function DocumentSigningRequestsPage() {
className="w-full py-3 px-4 text-sm text-txt-secondary hover:bg-level-1 transition-colors text-left flex items-center gap-2"
>
<span className="text-txt-tertiary">•••</span>
{sprintf(__("Show %s more documents"), hiddenBeforeCurrent)}
{sprintf(
__("Show %s more documents"),
hiddenBeforeCurrent
)}
</button>
)}
{/* Current document (if not first) */}
{!currentIsFirst && renderDocumentItem(currentDoc, currentDocIndex)}
{!currentIsFirst &&
renderDocumentItem(currentDoc, currentDocIndex)}
{/* Show more button for documents AFTER current (upcoming documents) */}
{hiddenAfterCurrent > 0 && (
@@ -381,7 +390,10 @@ export default function DocumentSigningRequestsPage() {
className="w-full py-3 px-4 text-sm text-txt-secondary hover:bg-level-1 transition-colors text-left flex items-center gap-2"
>
<span className="text-txt-tertiary">•••</span>
{sprintf(__("Show %s more documents"), hiddenAfterCurrent)}
{sprintf(
__("Show %s more documents"),
hiddenAfterCurrent
)}
</button>
)}
</>
@@ -390,9 +402,7 @@ export default function DocumentSigningRequestsPage() {
</div>
</Card>
<p className="text-txt-secondary text-sm mb-6">
{__(
"Please review the document carefully before signing."
)}
{__("Please review the document carefully before signing.")}
</p>
</>
)}
@@ -421,10 +431,7 @@ export default function DocumentSigningRequestsPage() {
</>
)}
{currentDoc.signed && !isLastDocument && (
<Button
onClick={handleNextDocument}
className="h-10 w-full mt-4"
>
<Button onClick={handleNextDocument} className="h-10 w-full mt-4">
{__("Next Document")}
</Button>
)}

View File

@@ -4,7 +4,6 @@ import { useTranslate } from "@probo/i18n";
import { z } from "zod";
import { useFormWithSchema } from "/hooks/useFormWithSchema";
import { usePageTitle } from "@probo/hooks";
import { buildEndpoint } from "/providers/RelayProviders";
import { useState } from "react";
const schema = z.object({
@@ -22,17 +21,14 @@ export default function ForgotPasswordPage() {
});
const onSubmit = handleSubmit(async (data) => {
const response = await fetch(
buildEndpoint("/connect/forget-password"),
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
credentials: "include",
body: JSON.stringify(data),
}
);
const response = await fetch("/connect/forget-password", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
credentials: "include",
body: JSON.stringify(data),
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));

View File

@@ -3,7 +3,6 @@ import { Button, Field, IconChevronLeft, useToast } from "@probo/ui";
import type { FormEventHandler } from "react";
import { useState } from "react";
import { Link, useSearchParams } from "react-router";
import { buildEndpoint } from "/providers/RelayProviders";
export default function LoginPage() {
const { __ } = useTranslate();
@@ -33,7 +32,7 @@ export default function LoginPage() {
setIsLoading(true);
try {
const res = await fetch(buildEndpoint("/connect/login"), {
const res = await fetch("/connect/login", {
method: "POST",
headers: {
"Content-Type": "application/json",
@@ -68,7 +67,7 @@ export default function LoginPage() {
setIsChecking(true);
try {
const res = await fetch(buildEndpoint("/connect/check-sso"), {
const res = await fetch("/connect/check-sso", {
method: "POST",
headers: {
"Content-Type": "application/json",
@@ -78,15 +77,15 @@ export default function LoginPage() {
if (!res.ok) {
const error = await res.json();
throw new Error(error.message || __("SSO not available for this email domain"));
throw new Error(
error.message || __("SSO not available for this email domain")
);
}
const data = await res.json();
if (data.ssoAvailable && data.samlConfigId) {
window.location.href = buildEndpoint(
`/connect/saml/login/${data.samlConfigId}`
);
window.location.href = `/connect/saml/login/${data.samlConfigId}`;
} else {
throw new Error(__("SSO not available for this email domain"));
}
@@ -115,10 +114,7 @@ export default function LoginPage() {
{__("Choose your login method")}
</p>
<Button
className="w-full"
onClick={() => setMode("password")}
>
<Button className="w-full" onClick={() => setMode("password")}>
{__("Login with Email")}
</Button>
@@ -146,7 +142,10 @@ export default function LoginPage() {
<div className="text-center mt-6 text-sm text-txt-secondary">
{__("Don't have an account ?")}{" "}
<Link to="/auth/register" className="underline hover:text-txt-primary">
<Link
to="/auth/register"
className="underline hover:text-txt-primary"
>
{__("Register")}
</Link>
</div>
@@ -206,7 +205,10 @@ export default function LoginPage() {
<div className="text-center mt-6 text-sm text-txt-secondary">
{__("Don't have an account ?")}{" "}
<Link to="/auth/register" className="underline hover:text-txt-primary">
<Link
to="/auth/register"
className="underline hover:text-txt-primary"
>
{__("Register")}
</Link>
</div>
@@ -235,9 +237,7 @@ export default function LoginPage() {
<span className="text-sm">{__("Back")}</span>
</button>
<h1 className="text-center text-2xl font-bold">
{__("Login with SSO")}
</h1>
<h1 className="text-center text-2xl font-bold">{__("Login with SSO")}</h1>
<p className="text-center text-txt-tertiary mt-1 mb-6">
{__("Enter your work email to continue with SSO")}
</p>

View File

@@ -4,7 +4,6 @@ import { useTranslate } from "@probo/i18n";
import { z } from "zod";
import { useFormWithSchema } from "/hooks/useFormWithSchema";
import { usePageTitle } from "@probo/hooks";
import { buildEndpoint } from "/providers/RelayProviders";
const schema = z.object({
email: z.string().email(),
@@ -25,17 +24,14 @@ export default function RegisterPage() {
});
const onSubmit = handleSubmit(async (data) => {
const response = await fetch(
buildEndpoint("/connect/register"),
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
credentials: "include",
body: JSON.stringify(data),
const response = await fetch("/connect/register", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
);
credentials: "include",
body: JSON.stringify(data),
});
// Registration failed
if (!response.ok) {

View File

@@ -4,7 +4,6 @@ import { useTranslate } from "@probo/i18n";
import { z } from "zod";
import { useFormWithSchema } from "/hooks/useFormWithSchema";
import { usePageTitle } from "@probo/hooks";
import { buildEndpoint } from "/providers/RelayProviders";
const schema = z
.object({
@@ -20,15 +19,12 @@ export default function ResetPasswordPage() {
const { __ } = useTranslate();
const navigate = useNavigate();
const { toast } = useToast();
const { register, handleSubmit, formState } = useFormWithSchema(
schema,
{
defaultValues: {
password: "",
confirmPassword: "",
},
}
);
const { register, handleSubmit, formState } = useFormWithSchema(schema, {
defaultValues: {
password: "",
confirmPassword: "",
},
});
const onSubmit = handleSubmit(async (data) => {
const searchParams = new URLSearchParams(location.search);
@@ -43,20 +39,17 @@ export default function ResetPasswordPage() {
return;
}
const response = await fetch(
buildEndpoint("/connect/reset-password"),
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
credentials: "include",
body: JSON.stringify({
token: token,
password: data.password,
}),
}
);
const response = await fetch("/connect/reset-password", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
credentials: "include",
body: JSON.stringify({
token: token,
password: data.password,
}),
});
// Reset failed
if (!response.ok) {

View File

@@ -4,7 +4,6 @@ import { useTranslate } from "@probo/i18n";
import { z } from "zod";
import { useFormWithSchema } from "/hooks/useFormWithSchema";
import { usePageTitle } from "@probo/hooks";
import { buildEndpoint } from "/providers/RelayProviders";
import { useEffect } from "react";
const schema = z.object({
@@ -50,21 +49,18 @@ export default function SignupFromInvitationPage() {
return;
}
const response = await fetch(
buildEndpoint("/connect/signup-from-invitation"),
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
credentials: "include",
body: JSON.stringify({
token: token,
password: data.password,
fullName: data.fullName,
}),
}
);
const response = await fetch("/connect/signup-from-invitation", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
credentials: "include",
body: JSON.stringify({
token: token,
password: data.password,
fullName: data.fullName,
}),
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
@@ -78,7 +74,9 @@ export default function SignupFromInvitationPage() {
toast({
title: __("Success"),
description: __("Account created successfully. Please accept your invitation to join the organization."),
description: __(
"Account created successfully. Please accept your invitation to join the organization."
),
variant: "success",
});
navigate("/", { replace: true });

View File

@@ -1,242 +0,0 @@
import {
Environment,
type FetchFunction,
Network,
RecordSource,
Store,
} from "relay-runtime";
import { GraphQLError } from "graphql";
import type { PropsWithChildren } from "react";
import { RelayEnvironmentProvider } from "react-relay";
export class UnAuthenticatedError extends Error {
constructor(message?: string) {
super(message || "UNAUTHENTICATED");
this.name = "UnAuthenticatedError";
}
}
export class InternalServerError extends Error {
constructor() {
super("INTERNAL_SERVER_ERROR");
this.name = "InternalServerError";
}
}
export class AuthenticationRequiredError extends Error {
public redirectUrl: string;
public requiresSaml: boolean;
public organizationId: string;
public samlConfigId?: string;
constructor(extensions: {
redirectUrl: string;
requiresSaml: boolean;
organizationId: string;
samlConfigId?: string;
}) {
super("AUTHENTICATION_REQUIRED");
this.name = "AuthenticationRequiredError";
this.redirectUrl = extensions.redirectUrl;
this.requiresSaml = extensions.requiresSaml;
this.organizationId = extensions.organizationId;
this.samlConfigId = extensions.samlConfigId;
}
}
export class UnauthorizedError extends Error {
constructor(message?: string) {
super(message || "UNAUTHORIZED");
this.name = "UnauthorizedError";
}
}
export class ForbiddenError extends Error {
constructor(message?: string) {
super(message || "FORBIDDEN");
this.name = "ForbiddenError";
}
}
export class InvalidError extends Error {
field?: string;
cause?: string;
constructor(message?: string, field?: string, cause?: string) {
super(message || "INVALID");
this.name = "InvalidError";
this.field = field;
this.cause = cause;
}
}
export function buildEndpoint(path: string): string {
const host = import.meta.env.VITE_API_URL;
if (!host) {
return path;
}
const formattedHost =
host.startsWith("http://") || host.startsWith("https://")
? host
: `https://${host}`;
const url = new URL(formattedHost);
if (path) {
url.pathname = path.startsWith("/") ? path : `/${path}`;
}
return url.toString();
}
const hasUnauthenticatedError = (error: GraphQLError) =>
error.extensions?.code == "UNAUTHENTICATED";
const hasAuthenticationRequiredError = (error: GraphQLError) =>
error.extensions?.code == "AUTHENTICATION_REQUIRED";
const hasUnauthorizedError = (error: GraphQLError) =>
error.extensions?.code == "UNAUTHORIZED";
const hasForbiddenError = (error: GraphQLError) =>
error.extensions?.code == "FORBIDDEN";
const hasInvalidError = (error: GraphQLError) =>
error.extensions?.code == "INVALID_REQUEST";
const fetchRelay: FetchFunction = async (
request,
variables,
_,
uploadables
) => {
const requestInit: RequestInit = {
method: "POST",
credentials: "include",
headers: {},
};
if (uploadables) {
const formData = new FormData();
formData.append(
"operations",
JSON.stringify({
operationName: request.name,
query: request.text,
variables: variables,
})
);
const uploadableMap: {
[key: string]: string[];
} = {};
Object.keys(uploadables).forEach((key, index) => {
uploadableMap[index] = [`variables.${key}`];
});
formData.append("map", JSON.stringify(uploadableMap));
Object.keys(uploadables).forEach((key, index) => {
formData.append(index.toString(), uploadables[key]);
});
requestInit.body = formData;
} else {
requestInit.headers = {
Accept:
"application/graphql-response+json; charset=utf-8, application/json; charset=utf-8",
"Content-Type": "application/json",
};
requestInit.body = JSON.stringify({
operationName: request.name,
query: request.text,
variables,
});
}
const response = await fetch(
buildEndpoint("/api/console/v1/query"),
requestInit
);
if (response.status === 500) {
throw new InternalServerError();
}
const json = await response.json();
if (json.errors) {
const errors = json.errors as GraphQLError[];
const unauthenticatedError = errors.find(hasUnauthenticatedError);
if (unauthenticatedError) {
throw new UnAuthenticatedError(unauthenticatedError.message);
}
const authRequiredError = errors.find(hasAuthenticationRequiredError);
if (authRequiredError?.extensions) {
const { redirectUrl, requiresSaml, organizationId, samlConfigId } = authRequiredError.extensions;
throw new AuthenticationRequiredError({
redirectUrl: redirectUrl as string,
requiresSaml: requiresSaml as boolean,
organizationId: organizationId as string,
samlConfigId: samlConfigId as string | undefined,
});
}
const unauthorizedError = errors.find(hasUnauthorizedError);
if (unauthorizedError) {
throw new UnauthorizedError(unauthorizedError.message);
}
const forbiddenError = errors.find(hasForbiddenError);
if (forbiddenError) {
throw new ForbiddenError(forbiddenError.message);
}
const invalidError = errors.find(hasInvalidError);
if (invalidError) {
throw new InvalidError(
invalidError.message,
invalidError.extensions.field as string ?? "",
invalidError.extensions.cause as string ?? "",
);
}
}
return json;
};
const source = new RecordSource();
const store = new Store(source, {
queryCacheExpirationTime: 1 * 60 * 1000,
gcReleaseBufferSize: 20,
});
export const relayEnvironment = new Environment({
network: Network.create(fetchRelay),
store,
});
export const clearRelayStore = () => {
const source = relayEnvironment.getStore().getSource();
if (source instanceof Map) {
source.clear();
}
};
/**
* Provider for relay with the probo environment
*/
export function RelayProvider({ children }: PropsWithChildren) {
return (
<RelayEnvironmentProvider environment={relayEnvironment}>
{children}
</RelayEnvironmentProvider>
);
}

View File

@@ -7,12 +7,6 @@ import {
import { MainLayout } from "./layouts/MainLayout";
import { EmployeeLayout } from "./layouts/EmployeeLayout";
import { AuthLayout, CenteredLayout, CenteredLayoutSkeleton } from "@probo/ui";
import {
relayEnvironment,
UnAuthenticatedError,
UnauthorizedError,
ForbiddenError,
} from "./providers/RelayProviders";
import { PageSkeleton } from "./components/skeletons/PageSkeleton.tsx";
import { loadQuery } from "react-relay";
import { riskRoutes } from "./routes/riskRoutes.ts";
@@ -37,12 +31,24 @@ import { rightsRequestRoutes } from "./routes/rightsRequestRoutes.ts";
import { processingActivityRoutes } from "./routes/processingActivityRoutes.ts";
import { statesOfApplicabilityRoutes } from "./routes/statesOfApplicabilityRoutes.ts";
import { lazy } from "@probo/react-lazy";
import { loaderFromQueryLoader, routeFromAppRoute, withQueryRef, type AppRoute } from "@probo/routes";
import {
loaderFromQueryLoader,
routeFromAppRoute,
withQueryRef,
type AppRoute,
} from "@probo/routes";
import { employeeDocumentsQuery } from "./pages/organizations/employee/EmployeeDocumentsPage";
import { employeeDocumentSignatureQuery } from "./pages/organizations/employee/EmployeeDocumentSignaturePage";
import { Role } from "@probo/helpers";
import { PermissionsContext } from "./providers/PermissionsContext";
import { use } from "react";
import { RelayEnvironmentProvider } from "react-relay";
import { connectEnvironment, consoleEnvironment } from "./environments.ts";
import {
ForbiddenError,
UnAuthenticatedError,
UnauthorizedError,
} from "@probo/relay";
/**
* Top level error boundary
@@ -68,7 +74,11 @@ function ErrorBoundary({ error: propsError }: { error?: string }) {
const routes = [
{
path: "/auth",
Component: AuthLayout,
Component: () => (
<RelayEnvironmentProvider environment={connectEnvironment}>
<AuthLayout />
</RelayEnvironmentProvider>
),
children: [
{
path: "login",
@@ -98,7 +108,11 @@ const routes = [
},
{
path: "/",
Component: CenteredLayout,
Component: () => (
<RelayEnvironmentProvider environment={consoleEnvironment}>
<CenteredLayout />
</RelayEnvironmentProvider>
),
Fallback: CenteredLayoutSkeleton,
ErrorBoundary: ErrorBoundary,
children: [
@@ -132,35 +146,42 @@ const routes = [
{
path: "",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(
({ organizationId }) =>
loadQuery(relayEnvironment, employeeDocumentsQuery, {
organizationId: organizationId!,
})
loader: loaderFromQueryLoader(({ organizationId }) =>
loadQuery(consoleEnvironment, employeeDocumentsQuery, {
organizationId: organizationId!,
})
),
Component: withQueryRef(
lazy(
() => import("./pages/organizations/employee/EmployeeDocumentsPage")
)
),
Component: withQueryRef(lazy(
() => import("./pages/organizations/employee/EmployeeDocumentsPage")
)),
},
{
path: ":documentId",
Fallback: PageSkeleton,
ErrorBoundary: ErrorBoundary,
loader: loaderFromQueryLoader(
({ documentId }) =>
loadQuery(relayEnvironment, employeeDocumentSignatureQuery, {
documentId: documentId!,
})
loader: loaderFromQueryLoader(({ documentId }) =>
loadQuery(consoleEnvironment, employeeDocumentSignatureQuery, {
documentId: documentId!,
})
),
Component: withQueryRef(
lazy(
() =>
import("./pages/organizations/employee/EmployeeDocumentSignaturePage")
)
),
Component: withQueryRef(lazy(
() => import("./pages/organizations/employee/EmployeeDocumentSignaturePage")
)),
},
],
},
{
path: "/organizations/:organizationId",
Component: MainLayout,
Component: () => (
<RelayEnvironmentProvider environment={consoleEnvironment}>
<MainLayout />
</RelayEnvironmentProvider>
),
ErrorBoundary: ErrorBoundary,
children: [
{
@@ -180,13 +201,14 @@ const routes = [
{
path: "settings",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(
({ organizationId }) =>
loadQuery(relayEnvironment, organizationViewQuery, {
organizationId: organizationId!,
})
loader: loaderFromQueryLoader(({ organizationId }) =>
loadQuery(consoleEnvironment, organizationViewQuery, {
organizationId: organizationId!,
})
),
Component: withQueryRef(
lazy(() => import("./pages/organizations/SettingsPage"))
),
Component: withQueryRef(lazy(() => import("./pages/organizations/SettingsPage"))),
children: [
{
path: "",
@@ -196,19 +218,27 @@ const routes = [
},
{
path: "general",
Component: lazy(() => import("./pages/organizations/settings/GeneralSettingsTab")),
Component: lazy(
() => import("./pages/organizations/settings/GeneralSettingsTab")
),
},
{
path: "members",
Component: lazy(() => import("./pages/organizations/settings/MembersSettingsTab")),
Component: lazy(
() => import("./pages/organizations/settings/MembersSettingsTab")
),
},
{
path: "domain",
Component: lazy(() => import("./pages/organizations/settings/DomainSettingsTab")),
Component: lazy(
() => import("./pages/organizations/settings/DomainSettingsTab")
),
},
{
path: "saml-sso",
Component: lazy(() => import("./pages/organizations/settings/SAMLSettingsTab")),
Component: lazy(
() => import("./pages/organizations/settings/SAMLSettingsTab")
),
},
],
},

View File

@@ -1,5 +1,5 @@
import { loadQuery } from "react-relay";
import { relayEnvironment } from "/providers/RelayProviders";
import { consoleEnvironment } from "/environments";
import { PageSkeleton } from "/components/skeletons/PageSkeleton";
import { lazy } from "@probo/react-lazy";
import { assetsQuery, assetNodeQuery } from "../hooks/graph/AssetGraph";
@@ -12,7 +12,7 @@ export const assetRoutes = [
path: "assets",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId }) =>
loadQuery<AssetGraphListQuery>(relayEnvironment, assetsQuery, {
loadQuery<AssetGraphListQuery>(consoleEnvironment, assetsQuery, {
organizationId: organizationId,
snapshotId: null,
}),
@@ -23,7 +23,7 @@ export const assetRoutes = [
path: "snapshots/:snapshotId/assets",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId, snapshotId }) =>
loadQuery<AssetGraphListQuery>(relayEnvironment, assetsQuery, {
loadQuery<AssetGraphListQuery>(consoleEnvironment, assetsQuery, {
organizationId: organizationId,
snapshotId: snapshotId,
}),
@@ -34,7 +34,7 @@ export const assetRoutes = [
path: "assets/:assetId",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ assetId }) =>
loadQuery<AssetGraphNodeQuery>(relayEnvironment, assetNodeQuery, { assetId }),
loadQuery<AssetGraphNodeQuery>(consoleEnvironment, assetNodeQuery, { assetId }),
),
Component: withQueryRef(lazy(
() => import("/pages/organizations/assets/AssetDetailsPage"),
@@ -44,7 +44,7 @@ export const assetRoutes = [
path: "snapshots/:snapshotId/assets/:assetId",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ assetId }) =>
loadQuery<AssetGraphNodeQuery>(relayEnvironment, assetNodeQuery, { assetId }),
loadQuery<AssetGraphNodeQuery>(consoleEnvironment, assetNodeQuery, { assetId }),
),
Component: withQueryRef(lazy(
() => import("/pages/organizations/assets/AssetDetailsPage"),

View File

@@ -1,5 +1,5 @@
import { loadQuery } from "react-relay";
import { relayEnvironment } from "/providers/RelayProviders";
import { consoleEnvironment } from "/environments";
import { PageSkeleton } from "/components/skeletons/PageSkeleton";
import { lazy } from "@probo/react-lazy";
import { auditsQuery, auditNodeQuery } from "../hooks/graph/AuditGraph";
@@ -12,7 +12,7 @@ export const auditRoutes = [
path: "audits",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId }) =>
loadQuery<AuditGraphListQuery>(relayEnvironment, auditsQuery, { organizationId }),
loadQuery<AuditGraphListQuery>(consoleEnvironment, auditsQuery, { organizationId }),
),
Component: withQueryRef(lazy(
() => import("/pages/organizations/audits/AuditsPage")
@@ -22,7 +22,7 @@ export const auditRoutes = [
path: "audits/:auditId",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ auditId }) =>
loadQuery<AuditGraphNodeQuery>(relayEnvironment, auditNodeQuery, { auditId }),
loadQuery<AuditGraphNodeQuery>(consoleEnvironment, auditNodeQuery, { auditId }),
),
Component: withQueryRef(lazy(
() => import("/pages/organizations/audits/AuditDetailsPage")

View File

@@ -1,5 +1,5 @@
import { loadQuery } from "react-relay";
import { relayEnvironment } from "/providers/RelayProviders";
import { consoleEnvironment } from "/environments";
import { PageSkeleton } from "/components/skeletons/PageSkeleton";
import { lazy } from "@probo/react-lazy";
import { continualImprovementsQuery, continualImprovementNodeQuery } from "/hooks/graph/ContinualImprovementGraph";
@@ -12,7 +12,7 @@ export const continualImprovementRoutes = [
path: "continual-improvements",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId }) =>
loadQuery<ContinualImprovementGraphListQuery>(relayEnvironment, continualImprovementsQuery, {
loadQuery<ContinualImprovementGraphListQuery>(consoleEnvironment, continualImprovementsQuery, {
organizationId,
snapshotId: null
}),
@@ -25,7 +25,7 @@ export const continualImprovementRoutes = [
path: "snapshots/:snapshotId/continual-improvements",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId, snapshotId }) =>
loadQuery<ContinualImprovementGraphListQuery>(relayEnvironment, continualImprovementsQuery, {
loadQuery<ContinualImprovementGraphListQuery>(consoleEnvironment, continualImprovementsQuery, {
organizationId,
snapshotId,
}),
@@ -38,7 +38,7 @@ export const continualImprovementRoutes = [
path: "continual-improvements/:improvementId",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ improvementId }) =>
loadQuery<ContinualImprovementGraphNodeQuery>(relayEnvironment, continualImprovementNodeQuery, {
loadQuery<ContinualImprovementGraphNodeQuery>(consoleEnvironment, continualImprovementNodeQuery, {
continualImprovementId: improvementId!,
}),
),
@@ -50,7 +50,7 @@ export const continualImprovementRoutes = [
path: "snapshots/:snapshotId/continual-improvements/:improvementId",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ improvementId }) =>
loadQuery<ContinualImprovementGraphNodeQuery>(relayEnvironment, continualImprovementNodeQuery, {
loadQuery<ContinualImprovementGraphNodeQuery>(consoleEnvironment, continualImprovementNodeQuery, {
continualImprovementId: improvementId!,
}),
),

View File

@@ -1,5 +1,5 @@
import { loadQuery } from "react-relay";
import { relayEnvironment } from "/providers/RelayProviders";
import { consoleEnvironment } from "/environments";
import { PageSkeleton } from "/components/skeletons/PageSkeleton";
import { lazy } from "@probo/react-lazy";
import { dataQuery, datumNodeQuery } from "../hooks/graph/DatumGraph";
@@ -12,7 +12,7 @@ export const dataRoutes = [
path: "data",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId }) =>
loadQuery<DatumGraphListQuery>(relayEnvironment, dataQuery, {
loadQuery<DatumGraphListQuery>(consoleEnvironment, dataQuery, {
organizationId: organizationId,
snapshotId: null
}),
@@ -25,7 +25,7 @@ export const dataRoutes = [
path: "snapshots/:snapshotId/data",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId, snapshotId }) =>
loadQuery<DatumGraphListQuery>(relayEnvironment, dataQuery, {
loadQuery<DatumGraphListQuery>(consoleEnvironment, dataQuery, {
organizationId,
snapshotId,
}),
@@ -38,7 +38,7 @@ export const dataRoutes = [
path: "data/:dataId",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ dataId }) =>
loadQuery<DatumGraphNodeQuery>(relayEnvironment, datumNodeQuery, { dataId }),
loadQuery<DatumGraphNodeQuery>(consoleEnvironment, datumNodeQuery, { dataId }),
),
Component: withQueryRef(lazy(
() => import("../pages/organizations/data/DatumDetailsPage")
@@ -48,7 +48,7 @@ export const dataRoutes = [
path: "snapshots/:snapshotId/data/:dataId",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ dataId }) =>
loadQuery<DatumGraphNodeQuery>(relayEnvironment, datumNodeQuery, { dataId }),
loadQuery<DatumGraphNodeQuery>(consoleEnvironment, datumNodeQuery, { dataId }),
),
Component: withQueryRef(lazy(
() => import("../pages/organizations/data/DatumDetailsPage")

View File

@@ -1,6 +1,6 @@
import { Fragment } from "react";
import { loadQuery } from "react-relay";
import { relayEnvironment } from "/providers/RelayProviders";
import { consoleEnvironment } from "/environments";
import { documentsQuery } from "/hooks/graph/DocumentGraph";
import { documentNodeQuery } from "/hooks/graph/DocumentGraph";
import { PageSkeleton } from "/components/skeletons/PageSkeleton";
@@ -58,7 +58,7 @@ export const documentsRoutes = [
path: "documents",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId }) =>
loadQuery<DocumentGraphListQuery>(relayEnvironment, documentsQuery, { organizationId: organizationId! }),
loadQuery<DocumentGraphListQuery>(consoleEnvironment, documentsQuery, { organizationId: organizationId! }),
),
Component: withQueryRef(lazy(
() => import("/pages/organizations/documents/DocumentsPage"),
@@ -68,7 +68,7 @@ export const documentsRoutes = [
path: "documents/:documentId",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ documentId }) =>
loadQuery<DocumentGraphNodeQuery>(relayEnvironment, documentNodeQuery, { documentId: documentId! }),
loadQuery<DocumentGraphNodeQuery>(consoleEnvironment, documentNodeQuery, { documentId: documentId! }),
),
Component: withQueryRef(lazy(
() => import("../pages/organizations/documents/DocumentDetailPage"),

View File

@@ -1,6 +1,6 @@
import { loadQuery } from "react-relay";
import { PageSkeleton } from "/components/skeletons/PageSkeleton";
import { relayEnvironment } from "/providers/RelayProviders";
import { consoleEnvironment } from "/environments";
import {
frameworksQuery,
frameworkNodeQuery,
@@ -19,7 +19,7 @@ export const frameworkRoutes = [
path: "frameworks",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId }) =>
loadQuery<FrameworkGraphListQuery>(relayEnvironment, frameworksQuery, { organizationId: organizationId! }),
loadQuery<FrameworkGraphListQuery>(consoleEnvironment, frameworksQuery, { organizationId: organizationId! }),
),
Component: withQueryRef(lazy(
() => import("/pages/organizations/frameworks/FrameworksPage")
@@ -29,7 +29,7 @@ export const frameworkRoutes = [
path: "frameworks/:frameworkId",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ frameworkId }) =>
loadQuery<FrameworkGraphNodeQuery>(relayEnvironment, frameworkNodeQuery, { frameworkId: frameworkId! }),
loadQuery<FrameworkGraphNodeQuery>(consoleEnvironment, frameworkNodeQuery, { frameworkId: frameworkId! }),
),
Component: withQueryRef(lazy(
() => import("/pages/organizations/frameworks/FrameworkDetailPage")
@@ -43,7 +43,7 @@ export const frameworkRoutes = [
path: "controls/:controlId",
Fallback: ControlSkeleton,
loader: loaderFromQueryLoader(({ controlId }) =>
loadQuery<FrameworkGraphControlNodeQuery>(relayEnvironment, frameworkControlNodeQuery, { controlId: controlId! }),
loadQuery<FrameworkGraphControlNodeQuery>(consoleEnvironment, frameworkControlNodeQuery, { controlId: controlId! }),
),
Component: withQueryRef(lazy(
() => import("/pages/organizations/frameworks/FrameworkControlPage")

View File

@@ -1,6 +1,6 @@
import { Fragment } from "react";
import { loadQuery } from "react-relay";
import { relayEnvironment } from "/providers/RelayProviders";
import { consoleEnvironment } from "/environments";
import { measureNodeQuery, measuresQuery } from "/hooks/graph/MeasureGraph";
import { PageSkeleton } from "/components/skeletons/PageSkeleton";
import { redirect } from "react-router";
@@ -15,7 +15,7 @@ export const measureRoutes = [
path: "measures",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId }) =>
loadQuery<MeasureGraphListQuery>(relayEnvironment, measuresQuery, { organizationId: organizationId! }),
loadQuery<MeasureGraphListQuery>(consoleEnvironment, measuresQuery, { organizationId: organizationId! }),
),
Component: withQueryRef(lazy(() => import("/pages/organizations/measures/MeasuresPage"))),
children: [
@@ -29,7 +29,7 @@ export const measureRoutes = [
path: "measures/:measureId",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ measureId }) =>
loadQuery<MeasureGraphNodeQuery>(relayEnvironment, measureNodeQuery, { measureId: measureId! }),
loadQuery<MeasureGraphNodeQuery>(consoleEnvironment, measureNodeQuery, { measureId: measureId! }),
),
Component: withQueryRef(lazy(() => import("/pages/organizations/measures/MeasureDetailPage"))),
children: [

View File

@@ -1,6 +1,6 @@
import { Fragment } from "react";
import { loadQuery } from "react-relay";
import { relayEnvironment } from "/providers/RelayProviders";
import { consoleEnvironment } from "/environments";
import { meetingsQuery } from "/hooks/graph/MeetingGraph";
import { meetingNodeQuery } from "/hooks/graph/MeetingGraph";
import { PageSkeleton } from "/components/skeletons/PageSkeleton";
@@ -40,7 +40,7 @@ export const meetingsRoutes = [
path: "meetings",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId }) =>
loadQuery<MeetingGraphListQuery>(relayEnvironment, meetingsQuery, { organizationId }),
loadQuery<MeetingGraphListQuery>(consoleEnvironment, meetingsQuery, { organizationId }),
),
Component: withQueryRef(lazy(
() => import("/pages/organizations/meetings/MeetingsPage"),
@@ -50,7 +50,7 @@ export const meetingsRoutes = [
path: "meetings/:meetingId",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ meetingId }) =>
loadQuery<MeetingGraphNodeQuery>(relayEnvironment, meetingNodeQuery, { meetingId }),
loadQuery<MeetingGraphNodeQuery>(consoleEnvironment, meetingNodeQuery, { meetingId }),
),
Component: withQueryRef(lazy(
() => import("../pages/organizations/meetings/MeetingDetailPage"),

View File

@@ -1,5 +1,5 @@
import { loadQuery } from "react-relay";
import { relayEnvironment } from "/providers/RelayProviders";
import { consoleEnvironment } from "/environments";
import { PageSkeleton } from "/components/skeletons/PageSkeleton";
import { lazy } from "@probo/react-lazy";
import { nonconformitiesQuery, nonconformityNodeQuery } from "../hooks/graph/NonconformityGraph";
@@ -12,7 +12,7 @@ export const nonconformityRoutes = [
path: "nonconformities",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId }) =>
loadQuery<NonconformityGraphListQuery>(relayEnvironment, nonconformitiesQuery, {
loadQuery<NonconformityGraphListQuery>(consoleEnvironment, nonconformitiesQuery, {
organizationId,
snapshotId: null
}),
@@ -25,7 +25,7 @@ export const nonconformityRoutes = [
path: "snapshots/:snapshotId/nonconformities",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId, snapshotId }) =>
loadQuery<NonconformityGraphListQuery>(relayEnvironment, nonconformitiesQuery, {
loadQuery<NonconformityGraphListQuery>(consoleEnvironment, nonconformitiesQuery, {
organizationId,
snapshotId,
}),
@@ -38,7 +38,7 @@ export const nonconformityRoutes = [
path: "nonconformities/:nonconformityId",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ nonconformityId }) =>
loadQuery<NonconformityGraphNodeQuery>(relayEnvironment, nonconformityNodeQuery, {
loadQuery<NonconformityGraphNodeQuery>(consoleEnvironment, nonconformityNodeQuery, {
nonconformityId,
}),
),
@@ -50,7 +50,7 @@ export const nonconformityRoutes = [
path: "snapshots/:snapshotId/nonconformities/:nonconformityId",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ nonconformityId }) =>
loadQuery<NonconformityGraphNodeQuery>(relayEnvironment, nonconformityNodeQuery, {
loadQuery<NonconformityGraphNodeQuery>(consoleEnvironment, nonconformityNodeQuery, {
nonconformityId: nonconformityId
}),
),

View File

@@ -1,5 +1,5 @@
import { loadQuery } from "react-relay";
import { relayEnvironment } from "/providers/RelayProviders";
import { consoleEnvironment } from "/environments";
import { PageSkeleton } from "/components/skeletons/PageSkeleton";
import { lazy } from "@probo/react-lazy";
import { obligationsQuery, obligationNodeQuery } from "/hooks/graph/ObligationGraph";
@@ -12,7 +12,7 @@ export const obligationRoutes = [
path: "obligations",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId }) =>
loadQuery<ObligationGraphListQuery>(relayEnvironment, obligationsQuery, {
loadQuery<ObligationGraphListQuery>(consoleEnvironment, obligationsQuery, {
organizationId,
snapshotId: null
}),
@@ -25,7 +25,7 @@ export const obligationRoutes = [
path: "snapshots/:snapshotId/obligations",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId, snapshotId }) =>
loadQuery<ObligationGraphListQuery>(relayEnvironment, obligationsQuery, {
loadQuery<ObligationGraphListQuery>(consoleEnvironment, obligationsQuery, {
organizationId,
snapshotId,
}),
@@ -38,7 +38,7 @@ export const obligationRoutes = [
path: "obligations/:obligationId",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ obligationId }) =>
loadQuery<ObligationGraphNodeQuery>(relayEnvironment, obligationNodeQuery, {
loadQuery<ObligationGraphNodeQuery>(consoleEnvironment, obligationNodeQuery, {
obligationId,
}),
),
@@ -50,7 +50,7 @@ export const obligationRoutes = [
path: "snapshots/:snapshotId/obligations/:obligationId",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ obligationId }) =>
loadQuery<ObligationGraphNodeQuery>(relayEnvironment, obligationNodeQuery, {
loadQuery<ObligationGraphNodeQuery>(consoleEnvironment, obligationNodeQuery, {
obligationId,
}),
),

View File

@@ -1,6 +1,6 @@
import { lazy } from "@probo/react-lazy";
import { loadQuery } from "react-relay";
import { relayEnvironment } from "/providers/RelayProviders";
import { consoleEnvironment } from "/environments";
import { PageSkeleton } from "/components/skeletons/PageSkeleton.tsx";
import {
paginatedPeopleQuery,
@@ -16,7 +16,7 @@ export const peopleRoutes = [
path: "people",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId }) =>
loadQuery<PeopleGraphPaginatedQuery>(relayEnvironment, paginatedPeopleQuery, { organizationId: organizationId! }),
loadQuery<PeopleGraphPaginatedQuery>(consoleEnvironment, paginatedPeopleQuery, { organizationId: organizationId! }),
),
Component: withQueryRef(lazy(() => import("/pages/organizations/people/PeopleListPage"))),
},
@@ -24,7 +24,7 @@ export const peopleRoutes = [
path: "people/:peopleId",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ peopleId }) =>
loadQuery<PeopleGraphNodeQuery>(relayEnvironment, peopleNodeQuery, { peopleId: peopleId! }),
loadQuery<PeopleGraphNodeQuery>(consoleEnvironment, peopleNodeQuery, { peopleId: peopleId! }),
),
Component: withQueryRef(lazy(
() => import("/pages/organizations/people/PeopleDetailPage")

View File

@@ -1,5 +1,5 @@
import { loadQuery } from "react-relay";
import { relayEnvironment } from "/providers/RelayProviders";
import { consoleEnvironment } from "/environments";
import { PageSkeleton } from "/components/skeletons/PageSkeleton";
import { lazy } from "@probo/react-lazy";
import { processingActivitiesQuery, processingActivityNodeQuery } from "/hooks/graph/ProcessingActivityGraph";
@@ -12,7 +12,7 @@ export const processingActivityRoutes = [
path: "processing-activities",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId }) =>
loadQuery<ProcessingActivityGraphListQuery>(relayEnvironment, processingActivitiesQuery, {
loadQuery<ProcessingActivityGraphListQuery>(consoleEnvironment, processingActivitiesQuery, {
organizationId: organizationId!,
snapshotId: null
}),
@@ -25,7 +25,7 @@ export const processingActivityRoutes = [
path: "snapshots/:snapshotId/processing-activities",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId, snapshotId }) =>
loadQuery<ProcessingActivityGraphListQuery>(relayEnvironment, processingActivitiesQuery, {
loadQuery<ProcessingActivityGraphListQuery>(consoleEnvironment, processingActivitiesQuery, {
organizationId: organizationId!,
snapshotId,
}),
@@ -38,7 +38,7 @@ export const processingActivityRoutes = [
path: "processing-activities/:activityId",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ activityId }) =>
loadQuery<ProcessingActivityGraphNodeQuery>(relayEnvironment, processingActivityNodeQuery, {
loadQuery<ProcessingActivityGraphNodeQuery>(consoleEnvironment, processingActivityNodeQuery, {
processingActivityId: activityId!,
}),
),
@@ -50,7 +50,7 @@ export const processingActivityRoutes = [
path: "snapshots/:snapshotId/processing-activities/:activityId",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ activityId }) =>
loadQuery<ProcessingActivityGraphNodeQuery>(relayEnvironment, processingActivityNodeQuery, {
loadQuery<ProcessingActivityGraphNodeQuery>(consoleEnvironment, processingActivityNodeQuery, {
processingActivityId: activityId!,
}),
),

View File

@@ -1,7 +1,7 @@
import { Fragment } from "react";
import { loadQuery } from "react-relay";
import { RisksPageSkeleton } from "/components/skeletons/RisksPageSkeleton.tsx";
import { relayEnvironment } from "/providers/RelayProviders";
import { consoleEnvironment } from "/environments";
import { riskNodeQuery, risksQuery } from "/hooks/graph/RiskGraph";
import { PageSkeleton } from "/components/skeletons/PageSkeleton";
import { redirect } from "react-router";
@@ -16,7 +16,7 @@ export const riskRoutes = [
path: "risks",
Fallback: RisksPageSkeleton,
loader: loaderFromQueryLoader(({ organizationId }) =>
loadQuery<RiskGraphListQuery>(relayEnvironment, risksQuery, {
loadQuery<RiskGraphListQuery>(consoleEnvironment, risksQuery, {
organizationId: organizationId!,
snapshotId: null
}),
@@ -27,7 +27,7 @@ export const riskRoutes = [
path: "snapshots/:snapshotId/risks",
Fallback: RisksPageSkeleton,
loader: loaderFromQueryLoader(({ organizationId, snapshotId }) =>
loadQuery<RiskGraphListQuery>(relayEnvironment, risksQuery, {
loadQuery<RiskGraphListQuery>(consoleEnvironment, risksQuery, {
organizationId: organizationId!,
snapshotId: snapshotId!
}),
@@ -38,7 +38,7 @@ export const riskRoutes = [
path: "risks/:riskId",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ riskId }) =>
loadQuery<RiskGraphNodeQuery>(relayEnvironment, riskNodeQuery, { riskId: riskId! }),
loadQuery<RiskGraphNodeQuery>(consoleEnvironment, riskNodeQuery, { riskId: riskId! }),
),
Component: withQueryRef(lazy(() => import("/pages/organizations/risks/RiskDetailPage"))),
children: [
@@ -90,7 +90,7 @@ export const riskRoutes = [
path: "snapshots/:snapshotId/risks/:riskId",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ riskId }) =>
loadQuery<RiskGraphNodeQuery>(relayEnvironment, riskNodeQuery, { riskId: riskId! }),
loadQuery<RiskGraphNodeQuery>(consoleEnvironment, riskNodeQuery, { riskId: riskId! }),
),
Component: withQueryRef(lazy(() => import("/pages/organizations/risks/RiskDetailPage"))),
children: [

View File

@@ -1,6 +1,6 @@
import { loadQuery } from "react-relay";
import { PageSkeleton } from "/components/skeletons/PageSkeleton";
import { relayEnvironment } from "/providers/RelayProviders";
import { consoleEnvironment } from "/environments";
import { snapshotsQuery, snapshotNodeQuery } from "/hooks/graph/SnapshotGraph";
import { lazy } from "@probo/react-lazy";
import type { SnapshotGraphListQuery } from "/hooks/graph/__generated__/SnapshotGraphListQuery.graphql";
@@ -12,7 +12,7 @@ export const snapshotsRoutes = [
path: "snapshots",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId }) =>
loadQuery<SnapshotGraphListQuery>(relayEnvironment, snapshotsQuery, { organizationId }),
loadQuery<SnapshotGraphListQuery>(consoleEnvironment, snapshotsQuery, { organizationId }),
),
Component: withQueryRef(lazy(
() => import("/pages/organizations/snapshots/SnapshotsPage")
@@ -22,7 +22,7 @@ export const snapshotsRoutes = [
path: "snapshots/:snapshotId",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ snapshotId }) =>
loadQuery<SnapshotGraphNodeQuery>(relayEnvironment, snapshotNodeQuery, { snapshotId }),
loadQuery<SnapshotGraphNodeQuery>(consoleEnvironment, snapshotNodeQuery, { snapshotId }),
),
Component: withQueryRef(lazy(
() => import("/pages/organizations/snapshots/SnapshotDetailPage")

View File

@@ -1,7 +1,7 @@
import { lazy } from "@probo/react-lazy";
import { PageSkeleton } from "/components/skeletons/PageSkeleton";
import { loadQuery } from "react-relay";
import { relayEnvironment } from "/providers/RelayProviders";
import { consoleEnvironment } from "/environments";
import { tasksQuery } from "/hooks/graph/TaskGraph";
import { loaderFromQueryLoader, withQueryRef, type AppRoute } from "@probo/routes";
import type { TaskGraphQuery } from "/hooks/graph/__generated__/TaskGraphQuery.graphql";
@@ -10,7 +10,7 @@ export const taskRoutes = [
path: "tasks",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId }) =>
loadQuery<TaskGraphQuery>(relayEnvironment, tasksQuery, {
loadQuery<TaskGraphQuery>(consoleEnvironment, tasksQuery, {
organizationId,
}),
),

View File

@@ -1,5 +1,5 @@
import { loadQuery } from "react-relay";
import { relayEnvironment } from "/providers/RelayProviders";
import { consoleEnvironment } from "/environments";
import { PageSkeleton } from "/components/skeletons/PageSkeleton";
import { LinkCardSkeleton } from "/components/skeletons/LinkCardSkeleton";
import { lazy } from "@probo/react-lazy";
@@ -12,7 +12,7 @@ export const trustCenterRoutes = [
path: "trust-center",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId }) =>
loadQuery<TrustCenterGraphQuery>(relayEnvironment, trustCenterQuery, { organizationId }, { fetchPolicy: "network-only" }),
loadQuery<TrustCenterGraphQuery>(consoleEnvironment, trustCenterQuery, { organizationId }, { fetchPolicy: "network-only" }),
),
Component: withQueryRef(lazy(
() => import("/pages/organizations/trustCenter/TrustCenterPage")

View File

@@ -1,6 +1,6 @@
import { lazy } from "@probo/react-lazy";
import { loadQuery } from "react-relay";
import { relayEnvironment } from "/providers/RelayProviders";
import { consoleEnvironment } from "/environments";
import { PageSkeleton } from "/components/skeletons/PageSkeleton";
import { vendorNodeQuery, vendorsQuery } from "/hooks/graph/VendorGraph";
import { LinkCardSkeleton } from "/components/skeletons/LinkCardSkeleton";
@@ -13,7 +13,7 @@ export const vendorRoutes = [
path: "vendors",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId }) =>
loadQuery<VendorGraphListQuery>(relayEnvironment, vendorsQuery, {
loadQuery<VendorGraphListQuery>(consoleEnvironment, vendorsQuery, {
organizationId: organizationId!,
snapshotId: null
}),
@@ -24,7 +24,7 @@ export const vendorRoutes = [
path: "snapshots/:snapshotId/vendors",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId, snapshotId }) =>
loadQuery<VendorGraphListQuery>(relayEnvironment, vendorsQuery, {
loadQuery<VendorGraphListQuery>(consoleEnvironment, vendorsQuery, {
organizationId: organizationId!,
snapshotId
}),
@@ -35,7 +35,7 @@ export const vendorRoutes = [
path: "vendors/:vendorId",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ vendorId }) =>
loadQuery<VendorGraphNodeQuery>(relayEnvironment, vendorNodeQuery, {
loadQuery<VendorGraphNodeQuery>(consoleEnvironment, vendorNodeQuery, {
vendorId: vendorId!,
}),
),
@@ -100,7 +100,7 @@ export const vendorRoutes = [
path: "snapshots/:snapshotId/vendors/:vendorId",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ vendorId }) =>
loadQuery<VendorGraphNodeQuery>(relayEnvironment, vendorNodeQuery, {
loadQuery<VendorGraphNodeQuery>(consoleEnvironment, vendorNodeQuery, {
vendorId: vendorId!,
}),
),

View File

@@ -24,6 +24,7 @@ export default defineConfig({
},
resolve: {
alias: {
"/environments": fileURLToPath(new URL("./src/environments", import.meta.url)),
"/type": fileURLToPath(new URL("./src/type.ts", import.meta.url)),
"/components": fileURLToPath(
new URL("./src/components", import.meta.url),