diff --git a/package-lock.json b/package-lock.json index a3e3c391..d658090c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -74,7 +74,7 @@ "lucide-react": "^0.552.0", "maxmind": "5.0.1", "moment": "2.30.1", - "next": "15.5.6", + "next": "^15.5.7", "next-intl": "^4.4.0", "next-themes": "0.4.6", "nextjs-toploader": "^3.9.17", @@ -87,7 +87,7 @@ "pg": "^8.16.2", "posthog-node": "^5.11.2", "qrcode.react": "4.2.0", - "react": "19.2.0", + "react": "^19.2.1", "react-day-picker": "9.11.1", "react-dom": "19.2.0", "react-easy-sort": "^1.8.0", @@ -3925,9 +3925,9 @@ } }, "node_modules/@next/env": { - "version": "15.5.6", - "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.6.tgz", - "integrity": "sha512-3qBGRW+sCGzgbpc5TS1a0p7eNxnOarGVQhZxfvTdnV0gFI61lX7QNtQ4V1TSREctXzYn5NetbUsLvyqwLFJM6Q==", + "version": "15.5.7", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.7.tgz", + "integrity": "sha512-4h6Y2NyEkIEN7Z8YxkA27pq6zTkS09bUSYC0xjd0NpwFxjnIKeZEeH591o5WECSmjpUhLn3H2QLJcDye3Uzcvg==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { @@ -3940,9 +3940,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "15.5.6", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.6.tgz", - "integrity": "sha512-ES3nRz7N+L5Umz4KoGfZ4XX6gwHplwPhioVRc25+QNsDa7RtUF/z8wJcbuQ2Tffm5RZwuN2A063eapoJ1u4nPg==", + "version": "15.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.7.tgz", + "integrity": "sha512-IZwtxCEpI91HVU/rAUOOobWSZv4P2DeTtNaCdHqLcTJU4wdNXgAySvKa/qJCgR5m6KI8UsKDXtO2B31jcaw1Yw==", "cpu": [ "arm64" ], @@ -3956,9 +3956,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "15.5.6", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.6.tgz", - "integrity": "sha512-JIGcytAyk9LQp2/nuVZPAtj8uaJ/zZhsKOASTjxDug0SPU9LAM3wy6nPU735M1OqacR4U20LHVF5v5Wnl9ptTA==", + "version": "15.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.7.tgz", + "integrity": "sha512-UP6CaDBcqaCBuiq/gfCEJw7sPEoX1aIjZHnBWN9v9qYHQdMKvCKcAVs4OX1vIjeE+tC5EIuwDTVIoXpUes29lg==", "cpu": [ "x64" ], @@ -3972,9 +3972,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "15.5.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.6.tgz", - "integrity": "sha512-qvz4SVKQ0P3/Im9zcS2RmfFL/UCQnsJKJwQSkissbngnB/12c6bZTCB0gHTexz1s6d/mD0+egPKXAIRFVS7hQg==", + "version": "15.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.7.tgz", + "integrity": "sha512-NCslw3GrNIw7OgmRBxHtdWFQYhexoUCq+0oS2ccjyYLtcn1SzGzeM54jpTFonIMUjNbHmpKpziXnpxhSWLcmBA==", "cpu": [ "arm64" ], @@ -3988,9 +3988,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "15.5.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.6.tgz", - "integrity": "sha512-FsbGVw3SJz1hZlvnWD+T6GFgV9/NYDeLTNQB2MXoPN5u9VA9OEDy6fJEfePfsUKAhJufFbZLgp0cPxMuV6SV0w==", + "version": "15.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.7.tgz", + "integrity": "sha512-nfymt+SE5cvtTrG9u1wdoxBr9bVB7mtKTcj0ltRn6gkP/2Nu1zM5ei8rwP9qKQP0Y//umK+TtkKgNtfboBxRrw==", "cpu": [ "arm64" ], @@ -4004,9 +4004,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "15.5.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.6.tgz", - "integrity": "sha512-3QnHGFWlnvAgyxFxt2Ny8PTpXtQD7kVEeaFat5oPAHHI192WKYB+VIKZijtHLGdBBvc16tiAkPTDmQNOQ0dyrA==", + "version": "15.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.7.tgz", + "integrity": "sha512-hvXcZvCaaEbCZcVzcY7E1uXN9xWZfFvkNHwbe/n4OkRhFWrs1J1QV+4U1BN06tXLdaS4DazEGXwgqnu/VMcmqw==", "cpu": [ "x64" ], @@ -4020,9 +4020,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "15.5.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.6.tgz", - "integrity": "sha512-OsGX148sL+TqMK9YFaPFPoIaJKbFJJxFzkXZljIgA9hjMjdruKht6xDCEv1HLtlLNfkx3c5w2GLKhj7veBQizQ==", + "version": "15.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.7.tgz", + "integrity": "sha512-4IUO539b8FmF0odY6/SqANJdgwn1xs1GkPO5doZugwZ3ETF6JUdckk7RGmsfSf7ws8Qb2YB5It33mvNL/0acqA==", "cpu": [ "x64" ], @@ -4036,9 +4036,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "15.5.6", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.6.tgz", - "integrity": "sha512-ONOMrqWxdzXDJNh2n60H6gGyKed42Ieu6UTVPZteXpuKbLZTH4G4eBMsr5qWgOBA+s7F+uB4OJbZnrkEDnZ5Fg==", + "version": "15.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.7.tgz", + "integrity": "sha512-CpJVTkYI3ZajQkC5vajM7/ApKJUOlm6uP4BknM3XKvJ7VXAvCqSjSLmM0LKdYzn6nBJVSjdclx8nYJSa3xlTgQ==", "cpu": [ "arm64" ], @@ -4052,9 +4052,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "15.5.6", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.6.tgz", - "integrity": "sha512-pxK4VIjFRx1MY92UycLOOw7dTdvccWsNETQ0kDHkBlcFH1GrTLUjSiHU1ohrznnux6TqRHgv5oflhfIWZwVROQ==", + "version": "15.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.7.tgz", + "integrity": "sha512-gMzgBX164I6DN+9/PGA+9dQiwmTkE4TloBNx8Kv9UiGARsr9Nba7IpcBRA1iTV9vwlYnrE3Uy6I7Aj6qLjQuqw==", "cpu": [ "x64" ], @@ -16168,12 +16168,12 @@ "license": "MIT" }, "node_modules/next": { - "version": "15.5.6", - "resolved": "https://registry.npmjs.org/next/-/next-15.5.6.tgz", - "integrity": "sha512-zTxsnI3LQo3c9HSdSf91O1jMNsEzIXDShXd4wVdg9y5shwLqBXi4ZtUUJyB86KGVSJLZx0PFONvO54aheGX8QQ==", + "version": "15.5.7", + "resolved": "https://registry.npmjs.org/next/-/next-15.5.7.tgz", + "integrity": "sha512-+t2/0jIJ48kUpGKkdlhgkv+zPTEOoXyr60qXe68eB/pl3CMJaLeIGjzp5D6Oqt25hCBiBTt8wEeeAzfJvUKnPQ==", "license": "MIT", "dependencies": { - "@next/env": "15.5.6", + "@next/env": "15.5.7", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", @@ -16186,14 +16186,14 @@ "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "15.5.6", - "@next/swc-darwin-x64": "15.5.6", - "@next/swc-linux-arm64-gnu": "15.5.6", - "@next/swc-linux-arm64-musl": "15.5.6", - "@next/swc-linux-x64-gnu": "15.5.6", - "@next/swc-linux-x64-musl": "15.5.6", - "@next/swc-win32-arm64-msvc": "15.5.6", - "@next/swc-win32-x64-msvc": "15.5.6", + "@next/swc-darwin-arm64": "15.5.7", + "@next/swc-darwin-x64": "15.5.7", + "@next/swc-linux-arm64-gnu": "15.5.7", + "@next/swc-linux-arm64-musl": "15.5.7", + "@next/swc-linux-x64-gnu": "15.5.7", + "@next/swc-linux-x64-musl": "15.5.7", + "@next/swc-win32-arm64-msvc": "15.5.7", + "@next/swc-win32-x64-msvc": "15.5.7", "sharp": "^0.34.3" }, "peerDependencies": { @@ -20231,9 +20231,9 @@ } }, "node_modules/react": { - "version": "19.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", - "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==", + "version": "19.2.1", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.1.tgz", + "integrity": "sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw==", "license": "MIT", "engines": { "node": ">=0.10.0" diff --git a/package.json b/package.json index 4488d7da..7d7650dd 100644 --- a/package.json +++ b/package.json @@ -97,7 +97,7 @@ "lucide-react": "^0.552.0", "maxmind": "5.0.1", "moment": "2.30.1", - "next": "15.5.6", + "next": "15.5.7", "next-intl": "^4.4.0", "next-themes": "0.4.6", "nextjs-toploader": "^3.9.17", @@ -110,7 +110,7 @@ "pg": "^8.16.2", "posthog-node": "^5.11.2", "qrcode.react": "4.2.0", - "react": "19.2.0", + "react": "19.2.1", "react-day-picker": "9.11.1", "react-dom": "19.2.0", "react-easy-sort": "^1.8.0", @@ -138,8 +138,8 @@ "@dotenvx/dotenvx": "1.51.1", "@esbuild-plugins/tsconfig-paths": "0.1.2", "@react-email/preview-server": "4.3.2", - "@tanstack/react-query-devtools": "^5.90.2", "@tailwindcss/postcss": "^4.1.17", + "@tanstack/react-query-devtools": "^5.90.2", "@types/better-sqlite3": "7.6.12", "@types/cookie-parser": "1.4.10", "@types/cors": "2.8.19", @@ -149,9 +149,9 @@ "@types/jmespath": "^0.15.2", "@types/js-yaml": "4.0.9", "@types/jsonwebtoken": "^9.0.10", - "@types/nprogress": "^0.2.3", "@types/node": "24.10.1", "@types/nodemailer": "7.0.3", + "@types/nprogress": "^0.2.3", "@types/pg": "8.15.6", "@types/react": "19.2.2", "@types/react-dom": "19.2.2", diff --git a/src/app/[orgId]/settings/clients/[clientId]/credentials/page.tsx b/src/app/[orgId]/settings/clients/machine/[clientId]/credentials/page.tsx similarity index 88% rename from src/app/[orgId]/settings/clients/[clientId]/credentials/page.tsx rename to src/app/[orgId]/settings/clients/machine/[clientId]/credentials/page.tsx index f14d49e4..42120319 100644 --- a/src/app/[orgId]/settings/clients/[clientId]/credentials/page.tsx +++ b/src/app/[orgId]/settings/clients/machine/[clientId]/credentials/page.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState } from "react"; +import RegenerateCredentialsModal from "@app/components/RegenerateCredentialsModal"; import { SettingsContainer, SettingsSection, @@ -10,18 +10,23 @@ import { SettingsSectionTitle } from "@app/components/Settings"; import { Button } from "@app/components/ui/button"; -import { createApiClient, formatAxiosError } from "@app/lib/api"; -import { useEnvContext } from "@app/hooks/useEnvContext"; -import { toast } from "@app/hooks/useToast"; -import { useParams, useRouter } from "next/navigation"; -import { useTranslations } from "next-intl"; -import { PickClientDefaultsResponse } from "@server/routers/client"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger +} from "@app/components/ui/tooltip"; import { useClientContext } from "@app/hooks/useClientContext"; -import RegenerateCredentialsModal from "@app/components/RegenerateCredentialsModal"; -import { build } from "@server/build"; +import { useEnvContext } from "@app/hooks/useEnvContext"; import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext"; import { useSubscriptionStatusContext } from "@app/hooks/useSubscriptionStatusContext"; -import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@app/components/ui/tooltip"; +import { toast } from "@app/hooks/useToast"; +import { createApiClient } from "@app/lib/api"; +import { build } from "@server/build"; +import { PickClientDefaultsResponse } from "@server/routers/client"; +import { useTranslations } from "next-intl"; +import { useParams, useRouter } from "next/navigation"; +import { useState } from "react"; export default function CredentialsPage() { const { env } = useEnvContext(); @@ -32,7 +37,8 @@ export default function CredentialsPage() { const { client } = useClientContext(); const [modalOpen, setModalOpen] = useState(false); - const [clientDefaults, setClientDefaults] = useState(null); + const [clientDefaults, setClientDefaults] = + useState(null); const { licenseStatus, isUnlocked } = useLicenseStatusContext(); const subscription = useSubscriptionStatusContext(); @@ -44,18 +50,19 @@ export default function CredentialsPage() { return isEnterpriseNotLicensed || isSaasNotSubscribed; }; - const handleConfirmRegenerate = async () => { - const res = await api.get(`/org/${orgId}/pick-client-defaults`); if (res && res.status === 200) { const data = res.data.data; setClientDefaults(data); - await api.post(`/re-key/${client?.clientId}/regenerate-client-secret`, { - olmId: data.olmId, - secret: data.olmSecret, - }); + await api.post( + `/re-key/${client?.clientId}/regenerate-client-secret`, + { + olmId: data.olmId, + secret: data.olmSecret + } + ); toast({ title: t("credentialsSaved"), @@ -95,7 +102,8 @@ export default function CredentialsPage() {
@@ -121,4 +129,4 @@ export default function CredentialsPage() { /> ); -} \ No newline at end of file +} diff --git a/src/app/[orgId]/settings/clients/[clientId]/general/page.tsx b/src/app/[orgId]/settings/clients/machine/[clientId]/general/page.tsx similarity index 95% rename from src/app/[orgId]/settings/clients/[clientId]/general/page.tsx rename to src/app/[orgId]/settings/clients/machine/[clientId]/general/page.tsx index 3ad25ca1..63d88593 100644 --- a/src/app/[orgId]/settings/clients/[clientId]/general/page.tsx +++ b/src/app/[orgId]/settings/clients/machine/[clientId]/general/page.tsx @@ -1,40 +1,37 @@ "use client"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { z } from "zod"; import { Button } from "@/components/ui/button"; import { Form, FormControl, - FormDescription, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; -import { useClientContext } from "@app/hooks/useClientContext"; -import { useForm } from "react-hook-form"; -import { toast } from "@app/hooks/useToast"; -import { useRouter } from "next/navigation"; import { SettingsContainer, SettingsSection, - SettingsSectionHeader, - SettingsSectionTitle, - SettingsSectionDescription, SettingsSectionBody, + SettingsSectionDescription, + SettingsSectionFooter, SettingsSectionForm, - SettingsSectionFooter + SettingsSectionHeader, + SettingsSectionTitle } from "@app/components/Settings"; -import { formatAxiosError } from "@app/lib/api"; -import { createApiClient } from "@app/lib/api"; +import { useClientContext } from "@app/hooks/useClientContext"; import { useEnvContext } from "@app/hooks/useEnvContext"; -import { useEffect, useState } from "react"; -import { Tag, TagInput } from "@app/components/tags/tag-input"; -import { AxiosResponse } from "axios"; +import { toast } from "@app/hooks/useToast"; +import { createApiClient, formatAxiosError } from "@app/lib/api"; +import { zodResolver } from "@hookform/resolvers/zod"; import { ListSitesResponse } from "@server/routers/site"; +import { AxiosResponse } from "axios"; import { useTranslations } from "next-intl"; +import { useRouter } from "next/navigation"; +import { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { z } from "zod"; const GeneralFormSchema = z.object({ name: z.string().nonempty("Name is required") diff --git a/src/app/[orgId]/settings/clients/[clientId]/layout.tsx b/src/app/[orgId]/settings/clients/machine/[clientId]/layout.tsx similarity index 76% rename from src/app/[orgId]/settings/clients/[clientId]/layout.tsx rename to src/app/[orgId]/settings/clients/machine/[clientId]/layout.tsx index 257cb20f..a51a003d 100644 --- a/src/app/[orgId]/settings/clients/[clientId]/layout.tsx +++ b/src/app/[orgId]/settings/clients/machine/[clientId]/layout.tsx @@ -1,19 +1,19 @@ -import { internal } from "@app/lib/api"; -import { AxiosResponse } from "axios"; -import { authCookieHeader } from "@app/lib/api/cookies"; -import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; -import { GetClientResponse } from "@server/routers/client"; -import ClientInfoCard from "../../../../../components/ClientInfoCard"; -import ClientProvider from "@app/providers/ClientProvider"; -import { redirect } from "next/navigation"; +import ClientInfoCard from "@app/components/ClientInfoCard"; import { HorizontalTabs } from "@app/components/HorizontalTabs"; -import { getTranslations } from "next-intl/server"; +import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; +import { internal } from "@app/lib/api"; +import { authCookieHeader } from "@app/lib/api/cookies"; +import ClientProvider from "@app/providers/ClientProvider"; import { build } from "@server/build"; +import { GetClientResponse } from "@server/routers/client"; +import { AxiosResponse } from "axios"; +import { getTranslations } from "next-intl/server"; +import { redirect } from "next/navigation"; type SettingsLayoutProps = { children: React.ReactNode; params: Promise<{ clientId: number | string; orgId: string }>; -} +}; export default async function SettingsLayout(props: SettingsLayoutProps) { const params = await props.params; @@ -36,16 +36,17 @@ export default async function SettingsLayout(props: SettingsLayoutProps) { const navItems = [ { - title: t('general'), - href: `/{orgId}/settings/clients/{clientId}/general` + title: t("general"), + href: `/{orgId}/settings/clients/machine/{clientId}/general` }, - ...(build === 'enterprise' - ? [{ - title: t('credentials'), - href: `/{orgId}/settings/clients/{clientId}/credentials` - }, - ] - : []), + ...(build === "enterprise" + ? [ + { + title: t("credentials"), + href: `/{orgId}/settings/clients/machine/{clientId}/credentials` + } + ] + : []) ]; return ( @@ -58,9 +59,7 @@ export default async function SettingsLayout(props: SettingsLayoutProps) {
- - {children} - + {children}
diff --git a/src/app/[orgId]/settings/clients/[clientId]/page.tsx b/src/app/[orgId]/settings/clients/machine/[clientId]/page.tsx similarity index 67% rename from src/app/[orgId]/settings/clients/[clientId]/page.tsx rename to src/app/[orgId]/settings/clients/machine/[clientId]/page.tsx index cbe9fb97..c59f6920 100644 --- a/src/app/[orgId]/settings/clients/[clientId]/page.tsx +++ b/src/app/[orgId]/settings/clients/machine/[clientId]/page.tsx @@ -4,5 +4,7 @@ export default async function ClientPage(props: { params: Promise<{ orgId: string; clientId: number | string }>; }) { const params = await props.params; - redirect(`/${params.orgId}/settings/clients/${params.clientId}/general`); + redirect( + `/${params.orgId}/settings/clients/machine/${params.clientId}/general` + ); } diff --git a/src/app/[orgId]/settings/clients/create/page.tsx b/src/app/[orgId]/settings/clients/machine/create/page.tsx similarity index 100% rename from src/app/[orgId]/settings/clients/create/page.tsx rename to src/app/[orgId]/settings/clients/machine/create/page.tsx diff --git a/src/app/[orgId]/settings/resources/create/page.tsx b/src/app/[orgId]/settings/resources/proxy/create/page.tsx similarity index 100% rename from src/app/[orgId]/settings/resources/create/page.tsx rename to src/app/[orgId]/settings/resources/proxy/create/page.tsx diff --git a/src/components/EditInternalResourceDialog.tsx b/src/components/EditInternalResourceDialog.tsx index 231940cd..91ac57e5 100644 --- a/src/components/EditInternalResourceDialog.tsx +++ b/src/components/EditInternalResourceDialog.tsx @@ -1,6 +1,6 @@ "use client"; -import { useEffect, useState } from "react"; +import { useEffect, useMemo, useRef, useState } from "react"; import { Button } from "@app/components/ui/button"; import { Input } from "@app/components/ui/input"; import { @@ -45,7 +45,7 @@ import { ListClientsResponse } from "@server/routers/client/listClients"; import { Tag, TagInput } from "@app/components/tags/tag-input"; import { AxiosResponse } from "axios"; import { UserType } from "@server/types/UserTypes"; -import { useQuery } from "@tanstack/react-query"; +import { useQueries, useQuery } from "@tanstack/react-query"; import { orgQueries, resourceQueries } from "@app/lib/queries"; type InternalResourceData = { @@ -157,42 +157,92 @@ export default function EditInternalResourceDialog({ type FormData = z.infer; - const { data: rolesResponse = [] } = useQuery(orgQueries.roles({ orgId })); + const queries = useQueries({ + queries: [ + orgQueries.roles({ orgId }), + orgQueries.users({ orgId }), + orgQueries.clients({ + orgId, + filters: { + filter: "machine" + } + }), + resourceQueries.resourceUsers({ resourceId: resource.id }), + resourceQueries.resourceRoles({ resourceId: resource.id }), + resourceQueries.resourceClients({ resourceId: resource.id }) + ], + combine: (results) => { + const [ + rolesQuery, + usersQuery, + clientsQuery, + resourceUsersQuery, + resourceRolesQuery, + resourceClientsQuery + ] = results; - const allRoles = rolesResponse - .map((role) => ({ - id: role.roleId.toString(), - text: role.name - })) - .filter((role) => role.text !== "Admin"); + const allRoles = (rolesQuery.data ?? []) + .map((role) => ({ + id: role.roleId.toString(), + text: role.name + })) + .filter((role) => role.text !== "Admin"); - const { data: usersResponse = [] } = useQuery(orgQueries.users({ orgId })); - const { data: existingClients = [] } = useQuery( - resourceQueries.resourceUsers({ resourceId: resource.id }) - ); - const { data: clientsResponse = [] } = useQuery( - orgQueries.clients({ - orgId, - filters: { - filter: "machine" - } - }) - ); + const allUsers = (usersQuery.data ?? []).map((user) => ({ + id: user.id.toString(), + text: `${user.email || user.username}${user.type !== UserType.Internal ? ` (${user.idpName})` : ""}` + })); - const allUsers = usersResponse.map((user) => ({ - id: user.id.toString(), - text: `${user.email || user.username}${user.type !== UserType.Internal ? ` (${user.idpName})` : ""}` - })); + const machineClients = (clientsQuery.data ?? []) + .filter((client) => !client.userId) + .map((client) => ({ + id: client.clientId.toString(), + text: client.name + })); - const allClients = clientsResponse - .filter((client) => !client.userId) - .map((client) => ({ - id: client.clientId.toString(), - text: client.name - })); + const existingClients = (resourceClientsQuery.data ?? []).map( + (c: { clientId: number; name: string }) => ({ + id: c.clientId.toString(), + text: c.name + }) + ); - const hasMachineClients = - allClients.length > 0 || existingClients.length > 0; + const formRoles = (resourceRolesQuery.data ?? []) + .map((i) => ({ + id: i.roleId.toString(), + text: i.name + })) + .filter((role) => role.text !== "Admin"); + + const formUsers = (resourceUsersQuery.data ?? []).map((i) => ({ + id: i.userId.toString(), + text: `${i.email || i.username}${i.type !== UserType.Internal ? ` (${i.idpName})` : ""}` + })); + + return { + allRoles, + allUsers, + machineClients, + existingClients, + formRoles, + formUsers, + hasMachineClients: + machineClients.length > 0 || existingClients.length > 0, + isLoading: results.some((query) => query.isLoading) + }; + } + }); + + const { + allRoles, + allUsers, + machineClients, + existingClients, + formRoles, + formUsers, + hasMachineClients, + isLoading: loadingRolesUsers + } = queries; const [activeRolesTagIndex, setActiveRolesTagIndex] = useState< number | null @@ -203,7 +253,6 @@ export default function EditInternalResourceDialog({ const [activeClientsTagIndex, setActiveClientsTagIndex] = useState< number | null >(null); - const [loadingRolesUsers, setLoadingRolesUsers] = useState(false); const form = useForm({ resolver: zodResolver(formSchema), @@ -223,117 +272,6 @@ export default function EditInternalResourceDialog({ const mode = form.watch("mode"); - // const fetchRolesAndUsers = async () => { - // setLoadingRolesUsers(true); - // try { - // const [ - // // rolesResponse, - // resourceRolesResponse, - // usersResponse, - // resourceUsersResponse, - // clientsResponse - // ] = await Promise.all([ - // // api.get>( - // // `/org/${orgId}/roles` - // // ), - // api.get>( - // `/site-resource/${resource.id}/roles` - // ), - // api.get>( - // `/org/${orgId}/users` - // ), - // api.get>( - // `/site-resource/${resource.id}/users` - // ), - // api.get>( - // `/org/${orgId}/clients?filter=machine&limit=1000` - // ) - // ]); - - // let resourceClientsResponse: AxiosResponse< - // AxiosResponse - // >; - // try { - // resourceClientsResponse = await api.get< - // AxiosResponse - // >(`/site-resource/${resource.id}/clients`); - // } catch { - // resourceClientsResponse = { - // data: { - // data: { - // clients: [] - // } - // }, - // status: 200, - // statusText: "OK", - // headers: {} as any, - // config: {} as any - // } as any; - // } - - // // setAllRoles( - // // rolesResponse.data.data.roles - // // .map((role) => ({ - // // id: role.roleId.toString(), - // // text: role.name - // // })) - // // .filter((role) => role.text !== "Admin") - // // ); - - // form.setValue( - // "roles", - // resourceRolesResponse.data.data.roles - // .map((i) => ({ - // id: i.roleId.toString(), - // text: i.name - // })) - // .filter((role) => role.text !== "Admin") - // ); - - // form.setValue( - // "users", - // resourceUsersResponse.data.data.users.map((i) => ({ - // id: i.userId.toString(), - // text: `${i.email || i.username}${i.type !== UserType.Internal ? ` (${i.idpName})` : ""}` - // })) - // ); - - // const machineClients = clientsResponse.data.data.clients - // .filter((client) => !client.userId) - // .map((client) => ({ - // id: client.clientId.toString(), - // text: client.name - // })); - - // setAllClients(machineClients); - - // const existingClients = - // resourceClientsResponse.data.data.clients.map( - // (c: { clientId: number; name: string }) => ({ - // id: c.clientId.toString(), - // text: c.name - // }) - // ); - - // form.setValue("clients", existingClients); - - // // Show clients tag input if there are machine clients OR existing client access - // setHasMachineClients( - // machineClients.length > 0 || existingClients.length > 0 - // ); - // } catch (error) { - // console.error("Error fetching roles, users, and clients:", error); - // } finally { - // setLoadingRolesUsers(false); - // } - // }; - - // useEffect(() => { - // if (open) { - // fetchRolesAndUsers(); - // } - // }, [open, resource]); - const handleSubmit = async (data: FormData) => { setIsSubmitting(true); try { @@ -399,6 +337,18 @@ export default function EditInternalResourceDialog({ } }; + // Set form values only once after initial load + const hasInitialized = useRef(false); + + useEffect(() => { + if (!loadingRolesUsers && !hasInitialized.current) { + hasInitialized.current = true; + form.setValue("roles", formRoles); + form.setValue("users", formUsers); + form.setValue("clients", existingClients); + } + }, [loadingRolesUsers, formRoles, formUsers, existingClients, form]); + return ( @@ -414,7 +414,7 @@ export default function MachineClientsTable({ searchPlaceholder={t("resourcesSearch")} searchColumn="name" onAdd={() => - router.push(`/${orgId}/settings/clients/create`) + router.push(`/${orgId}/settings/clients/machine/create`) } addButtonText={t("createClient")} onRefresh={refreshData} diff --git a/src/components/ProxyResourcesTable.tsx b/src/components/ProxyResourcesTable.tsx index 8cce2e06..0e86874c 100644 --- a/src/components/ProxyResourcesTable.tsx +++ b/src/components/ProxyResourcesTable.tsx @@ -554,7 +554,7 @@ export default function ProxyResourcesTable({ searchPlaceholder={t("resourcesSearch")} searchColumn="name" onAdd={() => - router.push(`/${orgId}/settings/resources/create`) + router.push(`/${orgId}/settings/resources/proxy/create`) } addButtonText={t("resourceAdd")} onRefresh={refreshData} diff --git a/src/lib/queries.ts b/src/lib/queries.ts index 260209b2..7a782289 100644 --- a/src/lib/queries.ts +++ b/src/lib/queries.ts @@ -3,6 +3,7 @@ import type { ListClientsResponse } from "@server/routers/client"; import type { ListRolesResponse } from "@server/routers/role"; import type { ListSitesResponse } from "@server/routers/site"; import type { + ListSiteResourceClientsResponse, ListSiteResourceRolesResponse, ListSiteResourceUsersResponse } from "@server/routers/siteResource"; @@ -160,5 +161,16 @@ export const resourceQueries = { return res.data.data.roles; } + }), + resourceClients: ({ resourceId }: { resourceId: number }) => + queryOptions({ + queryKey: ["RESOURCES", resourceId, "ROLES"] as const, + queryFn: async ({ signal, meta }) => { + const res = await meta!.api.get< + AxiosResponse + >(`/site-resource/${resourceId}/clients`, { signal }); + + return res.data.data.clients; + } }) };