Fix @probo/trust lint errors

Signed-off-by: Émile Ré <nemile.re@gmail.com>
This commit is contained in:
Émile Ré
2025-12-04 16:55:09 +04:00
parent 57caa9d28f
commit 6462de597b
11 changed files with 58 additions and 195 deletions

View File

@@ -1,28 +0,0 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
export default tseslint.config(
{ ignores: ['dist'] },
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ['**/*.{ts,tsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
plugins: {
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
},
)

View File

@@ -0,0 +1,27 @@
// @ts-check
import js from "@eslint/js";
import globals from "globals";
import reactHooks from "eslint-plugin-react-hooks";
import tseslint from "typescript-eslint";
export default tseslint.config(
{ ignores: ["dist"] },
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ["**/*.{ts,tsx}"],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
parserOptions: {
tsconfigRootDir: import.meta.dirname,
},
},
plugins: {
"react-hooks": reactHooks,
},
rules: {
...reactHooks.configs.recommended.rules,
},
}
);

View File

@@ -16,6 +16,7 @@
"@probo/helpers": "1.0.0",
"@probo/hooks": "1.0.0",
"@probo/i18n": "1.0.0",
"@probo/routes": "^1.0.0",
"@probo/ui": "1.0.0",
"clsx": "^2.1.1",
"react": "^19.1.0",
@@ -41,7 +42,6 @@
"babel-plugin-relay": "^19.0.0",
"eslint": "^9.25.0",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.19",
"globals": "^16.0.0",
"graphql": "^16.11.0",
"prettier": "^3.5.3",

View File

@@ -1,19 +0,0 @@
import { useEffect, useRef } from "react";
/**
* Hook to handle cleanup after a delay
*
* Used for disposing the graphQL query when the component unmounts
*/
export function useCleanup(callback: () => void, delay: number) {
const timer = useRef<ReturnType<typeof setTimeout>>(null);
useEffect(() => {
if (timer.current) {
clearTimeout(timer.current);
}
return () => {
timer.current = setTimeout(callback, delay);
};
}, [callback, delay]);
}

View File

@@ -56,7 +56,7 @@ export function useMutationWithToasts<T extends MutationParameters>(
})
);
},
[mutate]
[mutate, toast, __, baseOptions]
);
return [mutateWithToast, isLoading] as const;

View File

@@ -175,7 +175,7 @@ function Subprocessors({
}
const hasAnyCountries = vendors.some((vendor) => {
const vendorData = vendor.node as any;
const vendorData = vendor.node;
return vendorData.countries && vendorData.countries.length > 0;
});

View File

@@ -2,18 +2,15 @@ import {
createBrowserRouter,
Navigate,
redirect,
type RouteObject,
useLoaderData,
useRouteError,
} from "react-router";
import { type ComponentType, Fragment, Suspense } from "react";
import { Fragment } from "react";
import {
relayEnvironment,
UnAuthenticatedError,
} from "./providers/RelayProviders";
import { loadQuery, type PreloadedQuery } from "react-relay";
import { useCleanup } from "./hooks/useDelayedEffect";
import { PageError } from "./components/PageError";
} from "./providers/RelayProviders.tsx";
import { loadQuery } from "react-relay";
import { PageError } from "./components/PageError.tsx";
import { MainLayout } from "/layouts/MainLayout";
import {
currentTrustGraphQuery,
@@ -24,15 +21,9 @@ import { OverviewPage } from "/pages/OverviewPage";
import { DocumentsPage } from "/pages/DocumentsPage";
import { SubprocessorsPage } from "/pages/SubprocessorsPage";
import { AccessPage } from "./pages/AccessPage.tsx";
import { TabSkeleton } from "./components/Skeletons/TabSkeleton";
import { MainSkeleton } from "./components/Skeletons/MainSkeleton";
export type AppRoute = Omit<RouteObject, "Component" | "children"> & {
Component?: ComponentType<any>;
children?: AppRoute[];
fallback?: ComponentType;
queryLoader?: (params: any) => PreloadedQuery<any>;
};
import { TabSkeleton } from "./components/Skeletons/TabSkeleton.tsx";
import { MainSkeleton } from "./components/Skeletons/MainSkeleton.tsx";
import { loaderFromQueryLoader, routeFromAppRoute, withQueryRef, type AppRoute } from "@probo/routes";
/**
* Top level error boundary
@@ -59,47 +50,45 @@ const routes = [
// Custom domain routes (subdomain-based)
{
path: "/overview",
queryLoader: () => loadQuery(relayEnvironment, currentTrustGraphQuery, {}),
Component: MainLayout,
fallback: MainSkeleton,
loader: loaderFromQueryLoader(() => loadQuery(relayEnvironment, currentTrustGraphQuery, {})),
Component: withQueryRef(MainLayout),
Fallback: MainSkeleton,
ErrorBoundary: ErrorBoundary,
children: [
{
path: "",
fallback: TabSkeleton,
Fallback: TabSkeleton,
Component: OverviewPage,
},
],
},
{
path: "/documents",
queryLoader: () => loadQuery(relayEnvironment, currentTrustGraphQuery, {}),
Component: MainLayout,
fallback: MainSkeleton,
loader: loaderFromQueryLoader(() => loadQuery(relayEnvironment, currentTrustGraphQuery, {})),
Component: withQueryRef(MainLayout),
Fallback: MainSkeleton,
ErrorBoundary: ErrorBoundary,
children: [
{
path: "",
queryLoader: () =>
loadQuery(relayEnvironment, currentTrustDocumentsQuery, {}),
fallback: TabSkeleton,
Component: DocumentsPage,
loader: loaderFromQueryLoader(() => loadQuery(relayEnvironment, currentTrustDocumentsQuery, {})),
Fallback: TabSkeleton,
Component: withQueryRef(DocumentsPage),
},
],
},
{
path: "/subprocessors",
queryLoader: () => loadQuery(relayEnvironment, currentTrustGraphQuery, {}),
Component: MainLayout,
fallback: MainSkeleton,
loader: loaderFromQueryLoader(() => loadQuery(relayEnvironment, currentTrustGraphQuery, {})),
Component: withQueryRef(MainLayout),
Fallback: MainSkeleton,
ErrorBoundary: ErrorBoundary,
children: [
{
path: "",
queryLoader: () =>
loadQuery(relayEnvironment, currentTrustVendorsQuery, {}),
fallback: TabSkeleton,
Component: SubprocessorsPage,
loader: loaderFromQueryLoader(() => loadQuery(relayEnvironment, currentTrustVendorsQuery, {})),
Fallback: TabSkeleton,
Component: withQueryRef(SubprocessorsPage),
},
],
},
@@ -115,56 +104,6 @@ const routes = [
},
] satisfies AppRoute[];
/**
* Wrap components with suspense to handle lazy loading & relay loading states
*/
function routeTransformer({
fallback: FallbackComponent,
queryLoader,
...route
}: AppRoute): RouteObject {
let result = { ...route };
if (FallbackComponent && route.Component) {
const OriginalComponent = route.Component;
result = {
...result,
Component: (props) => (
<Suspense fallback={<FallbackComponent />}>
<OriginalComponent {...props} />
</Suspense>
),
};
}
if (queryLoader && route.Component) {
const OriginalComponent = route.Component;
result = {
...result,
loader: ({ params }) => {
const query = queryLoader(params as Record<string, string>);
return {
queryRef: query,
dispose: query.dispose,
};
},
Component: () => {
const { queryRef, dispose } = useLoaderData();
useCleanup(dispose, 1000);
return (
<Suspense fallback={FallbackComponent ? <FallbackComponent /> : null}>
<OriginalComponent queryRef={queryRef} />
</Suspense>
);
},
};
}
return {
...result,
children: route.children?.map(routeTransformer),
} as RouteObject;
}
// Detect basename from current URL path
// If URL starts with /trust/{slug}, extract that as the basename
// Otherwise, use "/" for custom domains
@@ -174,6 +113,6 @@ function getBasename(): string {
return trustMatch ? trustMatch[0] : "/";
}
export const router = createBrowserRouter(routes.map(routeTransformer), {
export const router = createBrowserRouter(routes.map(routeFromAppRoute), {
basename: getBasename(),
});

4
package-lock.json generated
View File

@@ -22,10 +22,11 @@
"version": "0.0.0",
"dependencies": {
"@hookform/resolvers": "^5.0.1",
"@probo/helpers": "1.0.0",
"@probo/helpers": "^1.0.0",
"@probo/hooks": "1.0.0",
"@probo/i18n": "1.0.0",
"@probo/react-lazy": "1.0.0",
"@probo/routes": "^1.0.0",
"@probo/ui": "1.0.0",
"@probo/vendors": "0.0.1",
"@tanstack/react-query": "^5.76.1",
@@ -72,6 +73,7 @@
"@probo/helpers": "1.0.0",
"@probo/hooks": "1.0.0",
"@probo/i18n": "1.0.0",
"@probo/routes": "^1.0.0",
"@probo/ui": "1.0.0",
"clsx": "^2.1.1",
"react": "^19.1.0",

View File

@@ -1,40 +0,0 @@
import { ComponentType, LazyExoticComponent } from "react";
import { EnvironmentProviderOptions, PreloadedQuery } from "react-relay";
import { LoaderFunction, LoaderFunctionArgs, RouteObject, useLoaderData } from "react-router";
import { OperationType } from "relay-runtime";
import { useCleanup } from "./useDelayedEffect";
export function withQueryRef<
TQuery extends OperationType,
TEnvironmentProviderOptions = EnvironmentProviderOptions
>(
Component: LazyExoticComponent<ComponentType<{ queryRef: PreloadedQuery<TQuery, TEnvironmentProviderOptions> }>>,
) {
return () => {
const { queryRef, dispose } = useLoaderData();
useCleanup(dispose, 1000);
return <Component queryRef={queryRef} />
}
}
export function loaderFromQueryLoader<
TQuery extends OperationType,
TEnvironmentProviderOptions = EnvironmentProviderOptions
>(
queryLoader: (params: Record<string, string>) => PreloadedQuery<TQuery, TEnvironmentProviderOptions>
): LoaderFunction {
return ({ params }: LoaderFunctionArgs) => {
const query = queryLoader(params as Record<string, string>);
return {
queryRef: query,
dispose: query.dispose,
};
}
}
export type AppRoute = Omit<RouteObject, "children"> & {
children?: AppRoute[];
fallback?: ComponentType;
}

View File

@@ -1,19 +0,0 @@
import { useEffect, useRef } from "react";
/**
* Hook to handle cleanup after a delay
*
* Used for disposing the graphQL query when the component unmounts
*/
export function useCleanup(callback: () => void, delay: number) {
const timer = useRef<ReturnType<typeof setTimeout>>(null);
useEffect(() => {
if (timer.current) {
clearTimeout(timer.current);
}
return () => {
timer.current = setTimeout(callback, delay);
};
}, [callback, delay]);
}

View File

@@ -3,3 +3,4 @@ export { useToggle } from "./useToggle";
export { useRefSync } from "./useRefSync";
export { useList } from "./useList";
export { useStateWithRef } from "./useStateWithRef";
export { useCleanup } from "./useCleanup";