Rewrite identity and access management
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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 ",
|
||||
|
||||
18
apps/console/src/environments.ts
Normal file
18
apps/console/src/environments.ts
Normal 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,
|
||||
});
|
||||
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
}: {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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(() => ({}));
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 });
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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")
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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!,
|
||||
}),
|
||||
),
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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: [
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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
|
||||
}),
|
||||
),
|
||||
|
||||
@@ -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,
|
||||
}),
|
||||
),
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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!,
|
||||
}),
|
||||
),
|
||||
|
||||
@@ -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: [
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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,
|
||||
}),
|
||||
),
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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!,
|
||||
}),
|
||||
),
|
||||
|
||||
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user