Fix apps/trust lint issues

Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
Émile Ré
2026-01-22 14:40:12 +04:00
parent dd23449cc0
commit 6ba2132cfe
23 changed files with 283 additions and 339 deletions

View File

@@ -153,7 +153,6 @@ const routes = [
children: [
{
index: true,
// Component: () => "hello world",
Component: lazy(
() =>
import("./pages/organizations/employee/EmployeeDocumentsPageLoader"),

View File

@@ -1,6 +1,17 @@
import { browser } from "@probo/eslint-config";
import { defineConfig } from "eslint/config";
import { configs } from "@probo/eslint-config";
export default [
{ ignores: ["dist", "eslint.config.mjs", "*.config.{js,mjs,ts}"] },
...browser(["./tsconfig.app.json", "./tsconfig.node.json"], import.meta.dirname),
];
export default defineConfig([
...configs.base,
...configs.ts,
...configs.react,
configs.languageOptions.browser,
...configs.stylistic,
{
languageOptions: {
parserOptions: {
tsConfigRootDir: import.meta.dirname,
},
},
},
]);

View File

@@ -68,10 +68,10 @@ export function AuditRow(props: { audit: AuditRowFragment$key }) {
audit.report?.hasUserRequestedAccess,
);
const [requestAccess, isRequestingAccess] =
useMutation<AuditRow_requestAccessMutation>(requestAccessMutation);
const [commitDownload, downloading] =
useMutationWithToasts<AuditRowDownloadMutation>(downloadMutation);
const [requestAccess, isRequestingAccess]
= useMutation<AuditRow_requestAccessMutation>(requestAccessMutation);
const [commitDownload, downloading]
= useMutationWithToasts<AuditRowDownloadMutation>(downloadMutation);
const handleRequestAccess = () => {
requestAccess({
@@ -106,11 +106,11 @@ export function AuditRow(props: { audit: AuditRowFragment$key }) {
});
};
const handleDownload = () => {
const handleDownload = async () => {
if (!audit.report?.id) {
return;
}
commitDownload({
await commitDownload({
variables: {
input: {
reportId: audit.report.id,
@@ -128,36 +128,40 @@ export function AuditRow(props: { audit: AuditRowFragment$key }) {
<IconMedal size={16} className="flex-none text-txt-tertiary" />
{audit.framework.name}
</div>
{audit.report && audit.report.isUserAuthorized ? (
<Button
className="w-full md:w-max"
variant="secondary"
disabled={downloading}
icon={downloading ? Spinner : IconArrowInbox}
onClick={handleDownload}
>
{__("Download")}
</Button>
) : viewer ? (
<Button
disabled={hasRequested || isRequestingAccess}
className="w-full md:w-max"
variant="secondary"
icon={IconLock}
onClick={handleRequestAccess}
>
{hasRequested ? __("Access requested") : __("Request access")}
</Button>
) : (
<Button
className="w-full md:w-max"
variant="secondary"
icon={IconLock}
to="/connect"
>
{hasRequested ? __("Access requested") : __("Request access")}
</Button>
)}
{audit.report && audit.report.isUserAuthorized
? (
<Button
className="w-full md:w-max"
variant="secondary"
disabled={downloading}
icon={downloading ? Spinner : IconArrowInbox}
onClick={() => void handleDownload()}
>
{__("Download")}
</Button>
)
: viewer
? (
<Button
disabled={hasRequested || isRequestingAccess}
className="w-full md:w-max"
variant="secondary"
icon={IconLock}
onClick={handleRequestAccess}
>
{hasRequested ? __("Access requested") : __("Request access")}
</Button>
)
: (
<Button
className="w-full md:w-max"
variant="secondary"
icon={IconLock}
to="/connect"
>
{hasRequested ? __("Access requested") : __("Request access")}
</Button>
)}
</div>
);
}

View File

@@ -56,10 +56,10 @@ export function DocumentRow(props: { document: DocumentRowFragment$key }) {
document.hasUserRequestedAccess,
);
const [requestAccess, isRequestingAccess] =
useMutation<DocumentRow_requestAccessMutation>(requestAccessMutation);
const [commitDownload, downloading] =
useMutationWithToasts<DocumentRowDownloadMutation>(downloadMutation);
const [requestAccess, isRequestingAccess]
= useMutation<DocumentRow_requestAccessMutation>(requestAccessMutation);
const [commitDownload, downloading]
= useMutationWithToasts<DocumentRowDownloadMutation>(downloadMutation);
const handleRequestAccess = () => {
requestAccess({
@@ -94,8 +94,8 @@ export function DocumentRow(props: { document: DocumentRowFragment$key }) {
});
};
const handleDownload = () => {
commitDownload({
const handleDownload = async () => {
await commitDownload({
variables: {
input: {
documentId: document.id,
@@ -113,36 +113,40 @@ export function DocumentRow(props: { document: DocumentRowFragment$key }) {
<IconPageTextLine size={16} className=" flex-none text-txt-tertiary" />
{document.title}
</div>
{document.isUserAuthorized ? (
<Button
className="w-full md:w-max"
variant="secondary"
disabled={downloading}
icon={downloading ? Spinner : IconArrowInbox}
onClick={handleDownload}
>
{__("Download")}
</Button>
) : viewer ? (
<Button
disabled={hasRequested || isRequestingAccess}
className="w-full md:w-max"
variant="secondary"
icon={IconLock}
onClick={handleRequestAccess}
>
{hasRequested ? __("Access requested") : __("Request access")}
</Button>
) : (
<Button
className="w-full md:w-max"
variant="secondary"
icon={IconLock}
to="/connect"
>
{hasRequested ? __("Access requested") : __("Request access")}
</Button>
)}
{document.isUserAuthorized
? (
<Button
className="w-full md:w-max"
variant="secondary"
disabled={downloading}
icon={downloading ? Spinner : IconArrowInbox}
onClick={() => void handleDownload()}
>
{__("Download")}
</Button>
)
: viewer
? (
<Button
disabled={hasRequested || isRequestingAccess}
className="w-full md:w-max"
variant="secondary"
icon={IconLock}
onClick={handleRequestAccess}
>
{hasRequested ? __("Access requested") : __("Request access")}
</Button>
)
: (
<Button
className="w-full md:w-max"
variant="secondary"
icon={IconLock}
to="/connect"
>
{hasRequested ? __("Access requested") : __("Request access")}
</Button>
)}
</div>
);
}

View File

@@ -60,8 +60,8 @@ export function NDADialog({
},
});
const handleSubmit = handleSubmitWrapper(({ fullName }) => {
commitSigning({
const handleSubmit = handleSubmitWrapper(async ({ fullName }) => {
await commitSigning({
variables: {
input: {
fullName,
@@ -98,7 +98,7 @@ export function NDADialog({
</Button>
</Card>
)}
<form onSubmit={handleSubmit}>
<form onSubmit={e => void handleSubmit(e)}>
<div className="mt-4">
<Field
required
@@ -129,7 +129,9 @@ export function NDADialog({
isMobile ? "mt-15" : "mt-30",
)}
>
Powered by <Logo withPicto className="h-6" />
Powered by
{" "}
<Logo withPicto className="h-6" />
</a>
</div>
{isDesktop && (

View File

@@ -35,8 +35,8 @@ export function OrganizationSidebar({
const isAuthenticated = !!use(Viewer);
const { toast } = useToast();
const [requestAllAccesses, isRequestingAccess] =
useMutation<OrganizationSidebar_requestAllAccessesMutation>(
const [requestAllAccesses, isRequestingAccess]
= useMutation<OrganizationSidebar_requestAllAccessesMutation>(
requestAllAccessesMutation,
);
@@ -75,15 +75,17 @@ export function OrganizationSidebar({
return (
<Card className="p-6 relative overflow-hidden border-b border-border-low isolate">
<div className="h-21 bg-[#044E4114] absolute top-0 left-0 right-0 -z-1"></div>
{trustCenter.organization.logoUrl ? (
<img
alt=""
src={trustCenter.organization.logoUrl}
className="size-24 rounded-2xl border border-border-mid shadow-mid bg-level-1"
/>
) : (
<div className="size-24 rounded-2xl border border-border-mid shadow-mid bg-level-1" />
)}
{trustCenter.organization.logoUrl
? (
<img
alt=""
src={trustCenter.organization.logoUrl}
className="size-24 rounded-2xl border border-border-mid shadow-mid bg-level-1"
/>
)
: (
<div className="size-24 rounded-2xl border border-border-mid shadow-mid bg-level-1" />
)}
<h1 className="text-2xl mt-6">{trustCenter.organization.name}</h1>
<p className="text-sm text-txt-secondary mt-1">
{trustCenter.organization.description}
@@ -137,7 +139,7 @@ export function OrganizationSidebar({
gridTemplateColumns: "repeat(auto-fit, 75px",
}}
>
{trustCenter.audits.edges.map((audit) => (
{trustCenter.audits.edges.map(audit => (
<AuditRowAvatar key={audit.node.id} audit={audit.node} />
))}
</div>
@@ -148,26 +150,28 @@ export function OrganizationSidebar({
)}
{/* Actions */}
{isAuthenticated ? (
<Button
disabled={isRequestingAccess}
variant="primary"
icon={IconLock}
className="w-full h-10"
onClick={handleRequestAllAccesses}
>
{__("Request access")}
</Button>
) : (
<Button
variant="primary"
icon={IconLock}
className="w-full h-10"
to="/connect"
>
{__("Request access")}
</Button>
)}
{isAuthenticated
? (
<Button
disabled={isRequestingAccess}
variant="primary"
icon={IconLock}
className="w-full h-10"
onClick={handleRequestAllAccesses}
>
{__("Request access")}
</Button>
)
: (
<Button
variant="primary"
icon={IconLock}
className="w-full h-10"
to="/connect"
>
{__("Request access")}
</Button>
)}
</div>
</Card>
);

View File

@@ -15,8 +15,8 @@ import { IconMinusLarge } from "@probo/ui/src/Atoms/Icons/IconMinusLarge.tsx";
// Worker for PDF.js
pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`;
const btnClass =
"size-8 grid place-items-center hover:bg-secondary-hover cursor-pointer rounded-sm disabled:opacity-30 transition-all";
const btnClass
= "size-8 grid place-items-center hover:bg-secondary-hover cursor-pointer rounded-sm disabled:opacity-30 transition-all";
export function PDFPreview({ src, name }: { src: string; name?: string }) {
const [numPages, setNumPages] = useState(0);
@@ -92,7 +92,10 @@ export function PDFPreview({ src, name }: { src: string; name?: string }) {
<IconChevronLeft size={16} />
</button>
<div>
{currentPage} / {numPages}
{currentPage}
{" "}
/
{numPages}
</div>
<button onClick={movePage(1)} className={btnClass}>
<IconChevronRight size={16} />
@@ -122,7 +125,7 @@ export function PDFPreview({ src, name }: { src: string; name?: string }) {
ref={documentRef}
>
{numPages === 0 && <Spinner className="mx-auto" />}
{times(numPages, (index) => (
{times(numPages, index => (
<Page
className="w-max h-max mx-auto shadow-mid"
key={index.toString()}

View File

@@ -25,14 +25,14 @@ export function PageError({ resetErrorBoundary, error: propsError }: Props) {
// Reset error boundary on page change
useEffect(() => {
if (
location.pathname !== baseLocation.current.pathname &&
resetErrorBoundary
location.pathname !== baseLocation.current.pathname
&& resetErrorBoundary
) {
resetErrorBoundary();
}
}, [location, resetErrorBoundary]);
if (!error || (error && error.toString().includes("PAGE_NOT_FOUND"))) {
if (!error || (error instanceof Error && error.message.includes("PAGE_NOT_FOUND"))) {
return (
<div className={classNames.wrapper}>
<h1 className={classNames.title}>
@@ -46,22 +46,21 @@ export function PageError({ resetErrorBoundary, error: propsError }: Props) {
);
}
if (
error
.toString()
if (error instanceof Error
&& error.message
.toLowerCase()
.match(/(token|expired|invalid|401|unauthorized)/)
) {
const isExpiredToken = error.toString().toLowerCase().includes("expired");
const isExpiredToken = error.message.toLowerCase().includes("expired");
const title = isExpiredToken
? __("Expired token")
: __("Invalid Access Link");
const description = isExpiredToken
? __(
"This access link has expired. Trust center access links are valid for 7 days for security reasons."
"This access link has expired. Trust center access links are valid for 7 days for security reasons.",
)
: __(
"This access link is not valid. It may have been revoked or the link might be incorrect."
"This access link is not valid. It may have been revoked or the link might be incorrect.",
);
return (
<div className={classNames.wrapper}>
@@ -81,7 +80,8 @@ export function PageError({ resetErrorBoundary, error: propsError }: Props) {
<summary className={classNames.description}>
{__("Something went wrong")}
</summary>
<p className={classNames.detail}>{error.toString()}</p>
{error instanceof Error
&& <p className={classNames.detail}>{error.message}</p>}
</details>
</div>
);

View File

@@ -58,12 +58,12 @@ export function TrustCenterFileRow(props: {
const file = useFragment(trustCenterFileRowFragment, props.file);
const [hasRequested, setHasRequested] = useState(file.hasUserRequestedAccess);
const [requestAccess, isRequestingAccess] =
useMutation<TrustCenterFileRow_requestAccessMutation>(
const [requestAccess, isRequestingAccess]
= useMutation<TrustCenterFileRow_requestAccessMutation>(
requestAccessMutation,
);
const [commitDownload, downloading] =
useMutationWithToasts<TrustCenterFileRowDownloadMutation>(downloadMutation);
const [commitDownload, downloading]
= useMutationWithToasts<TrustCenterFileRowDownloadMutation>(downloadMutation);
const handleRequestAccess = () => {
requestAccess({
@@ -98,8 +98,8 @@ export function TrustCenterFileRow(props: {
});
};
const handleDownload = () => {
commitDownload({
const handleDownload = async () => {
await commitDownload({
variables: {
input: {
trustCenterFileId: file.id,
@@ -117,36 +117,40 @@ export function TrustCenterFileRow(props: {
<IconPageTextLine size={16} className=" flex-none text-txt-tertiary" />
{file.name}
</div>
{file.isUserAuthorized ? (
<Button
className="w-full md:w-max"
variant="secondary"
disabled={downloading}
icon={downloading ? Spinner : IconArrowInbox}
onClick={handleDownload}
>
{__("Download")}
</Button>
) : viewer ? (
<Button
disabled={hasRequested || isRequestingAccess}
className="w-full md:w-max"
variant="secondary"
icon={IconLock}
onClick={handleRequestAccess}
>
{hasRequested ? __("Access requested") : __("Request access")}
</Button>
) : (
<Button
className="w-full md:w-max"
variant="secondary"
icon={IconLock}
to="/connect"
>
{hasRequested ? __("Access requested") : __("Request access")}
</Button>
)}
{file.isUserAuthorized
? (
<Button
className="w-full md:w-max"
variant="secondary"
disabled={downloading}
icon={downloading ? Spinner : IconArrowInbox}
onClick={() => void handleDownload()}
>
{__("Download")}
</Button>
)
: viewer
? (
<Button
disabled={hasRequested || isRequestingAccess}
className="w-full md:w-max"
variant="secondary"
icon={IconLock}
onClick={handleRequestAccess}
>
{hasRequested ? __("Access requested") : __("Request access")}
</Button>
)
: (
<Button
className="w-full md:w-max"
variant="secondary"
icon={IconLock}
to="/connect"
>
{hasRequested ? __("Access requested") : __("Request access")}
</Button>
)}
</div>
);
}

View File

@@ -25,35 +25,39 @@ export function VendorRow(props: { vendor: VendorRowFragment$key; hasAnyCountrie
return (
<div className="flex text-sm leading-tight gap-3 md:items-center">
{logo ? (
<img
src={logo}
className="size-8 md:size-6 flex-none rounded-lg"
alt=""
/>
) : (
<div className="size-8 md:size-6 flex-none rounded-lg" />
)}
{logo
? (
<img
src={logo}
className="size-8 md:size-6 flex-none rounded-lg"
alt=""
/>
)
: (
<div className="size-8 md:size-6 flex-none rounded-lg" />
)}
<div className={`flex flex-col md:grid ${gridCols} flex-1 gap-0.5`}>
<div>{vendor.name}</div>
{vendor.privacyPolicyUrl ? (
<a
href={vendor.privacyPolicyUrl}
target="_blank"
className={`flex gap-1 text-txt-info items-center hover:underline ${!props.hasAnyCountries ? 'md:justify-end' : ''}`}
>
<IconShield size={16} className="flex-none" />
<span>{__("Privacy")}</span>
</a>
) : (
<div></div>
)}
{vendor.privacyPolicyUrl
? (
<a
href={vendor.privacyPolicyUrl}
target="_blank"
className={`flex gap-1 text-txt-info items-center hover:underline ${!props.hasAnyCountries ? "md:justify-end" : ""}`}
>
<IconShield size={16} className="flex-none" />
<span>{__("Privacy")}</span>
</a>
)
: (
<div></div>
)}
{vendor.countries.length > 0 && (
<div className={`flex gap-1 text-txt-secondary items-center ${props.hasAnyCountries ? 'md:justify-end' : ''}`}>
<div className={`flex gap-1 text-txt-secondary items-center ${props.hasAnyCountries ? "md:justify-end" : ""}`}>
<IconPin size={16} className="flex-none" />
<span>
{vendor.countries
.map((country) => getCountryName(__, country))
.map(country => getCountryName(__, country))
.join(", ")}
</span>
</div>

View File

@@ -12,7 +12,7 @@ export function useMutationWithToasts<T extends MutationParameters>(
baseOptions?: {
onSuccess?: (response: T["response"]) => void;
errorMessage?: string;
}
},
) {
const [mutate, isLoading] = useMutation<T>(query);
const { toast } = useToast();
@@ -22,7 +22,7 @@ export function useMutationWithToasts<T extends MutationParameters>(
queryOptions: UseMutationConfig<T> & {
onSuccess?: (response: T["response"]) => void;
errorMessage?: string;
}
},
) => {
const options = { ...baseOptions, ...queryOptions };
return new Promise<void>((resolve, reject) =>
@@ -34,11 +34,11 @@ export function useMutationWithToasts<T extends MutationParameters>(
toast({
title: __("Error"),
description:
options.errorMessage ??
__("Failed to commit this operation."),
options.errorMessage
?? __("Failed to commit this operation."),
variant: "error",
});
reject(error);
reject(error instanceof Error ? error : new Error(__("Failed to commit this operation.")));
return;
}
options.onSuccess?.(response);
@@ -53,10 +53,10 @@ export function useMutationWithToasts<T extends MutationParameters>(
});
reject(error);
},
})
}),
);
},
[mutate, toast, __, baseOptions]
[mutate, toast, __, baseOptions],
);
return [mutateWithToast, isLoading] as const;

View File

@@ -21,10 +21,10 @@ export function MainLayout(props: Props) {
if (!trustCenter) {
return null;
}
const showNDADialog =
trustCenter.isViewerMember &&
!trustCenter.hasAcceptedNonDisclosureAgreement &&
trustCenter.ndaFileUrl;
const showNDADialog
= trustCenter.isViewerMember
&& !trustCenter.hasAcceptedNonDisclosureAgreement
&& trustCenter.ndaFileUrl;
return (
<Viewer value={data.viewer}>
<TrustCenterProvider trustCenter={trustCenter}>
@@ -51,7 +51,9 @@ export function MainLayout(props: Props) {
href="https://www.getprobo.com/"
className="flex gap-2 text-sm font-medium text-txt-tertiary items-center w-max mx-auto my-10"
>
{__("Powered by")} <Logo withPicto className="h-6" />
{__("Powered by")}
{" "}
<Logo withPicto className="h-6" />
</a>
</TrustCenterProvider>
</Viewer>

View File

@@ -23,5 +23,5 @@ createRoot(document.getElementById("root")!).render(
</TranslatorProvider>
</RelayProvider>
</QueryClientProvider>
</StrictMode>
</StrictMode>,
);

View File

@@ -18,16 +18,16 @@ export function DocumentsPage({ queryRef }: Props) {
const { __ } = useTranslate();
const data = usePreloadedQuery<TrustGraphCurrentDocumentsQuery>(
currentTrustDocumentsQuery,
queryRef
queryRef,
);
const documents =
data.currentTrustCenter?.documents.edges.map((edge) => edge.node) ?? [];
const files =
data.currentTrustCenter?.trustCenterFiles.edges.map((edge) => edge.node) ?? [];
const documentsPerType = groupBy(documents, (document) =>
documentTypeLabel(document.documentType, __)
const documents
= data.currentTrustCenter?.documents.edges.map(edge => edge.node) ?? [];
const files
= data.currentTrustCenter?.trustCenterFiles.edges.map(edge => edge.node) ?? [];
const documentsPerType = groupBy(documents, document =>
documentTypeLabel(document.documentType, __),
);
const filesPerCategory = groupBy(files, (file) => file.category);
const filesPerCategory = groupBy(files, file => file.category);
return (
<div>
<h2 className="font-medium mb-1">{__("Documents")}</h2>
@@ -38,7 +38,7 @@ export function DocumentsPage({ queryRef }: Props) {
{objectEntries(documentsPerType).map(([label, documents]) => (
<Fragment key={label}>
<RowHeader>{label}</RowHeader>
{documents.map((document) => (
{documents.map(document => (
<DocumentRow key={document.id} document={document} />
))}
</Fragment>
@@ -46,7 +46,7 @@ export function DocumentsPage({ queryRef }: Props) {
{objectEntries(filesPerCategory).map(([category, files]) => (
<Fragment key={category}>
<RowHeader>{category}</RowHeader>
{files.map((file) => (
{files.map(file => (
<TrustCenterFileRow key={file.id} file={file} />
))}
</Fragment>

View File

@@ -67,14 +67,14 @@ const overviewFragment = graphql`
export function OverviewPage() {
const { trustCenter } = useOutletContext<{
trustCenter: OverviewPageFragment$key &
TrustGraphCurrentQuery$data["currentTrustCenter"];
trustCenter: OverviewPageFragment$key
& TrustGraphCurrentQuery$data["currentTrustCenter"];
}>();
const fragment = useFragment(overviewFragment, trustCenter);
return (
<div>
<References
references={fragment.references.edges.map((edge) => edge.node)}
references={fragment.references.edges.map(edge => edge.node)}
/>
<Documents
audits={trustCenter.audits.edges}
@@ -106,12 +106,12 @@ function Documents({
}) {
const { __ } = useTranslate();
const documentsPerType = groupBy(
documents.map((edge) => edge.node),
(node) => documentTypeLabel(node.documentType, __),
documents.map(edge => edge.node),
node => documentTypeLabel(node.documentType, __),
);
const filesPerCategory = groupBy(
files.map((edge) => edge.node),
(node) => node.category,
files.map(edge => edge.node),
node => node.category,
);
const hasAudits = audits.length > 0;
const hasDocuments = hasAudits || documents.length > 0 || files.length > 0;
@@ -130,7 +130,7 @@ function Documents({
{audits.length > 0 && (
<>
<RowHeader>{__("Compliance")}</RowHeader>
{audits.map((audit) => (
{audits.map(audit => (
<AuditRow key={audit.node.id} audit={audit.node} />
))}
</>
@@ -138,7 +138,7 @@ function Documents({
{objectEntries(documentsPerType).map(([label, documents]) => (
<Fragment key={label}>
<RowHeader>{label}</RowHeader>
{documents.map((document) => (
{documents.map(document => (
<DocumentRow key={document.id} document={document} />
))}
</Fragment>
@@ -146,7 +146,7 @@ function Documents({
{objectEntries(filesPerCategory).map(([category, files]) => (
<Fragment key={category}>
<RowHeader>{category}</RowHeader>
{files.map((file) => (
{files.map(file => (
<TrustCenterFileRow key={file.id} file={file} />
))}
</Fragment>
@@ -189,7 +189,7 @@ function Subprocessors({
)}
</p>
<Rows className="mb-8 *:py-5">
{vendors.map((vendor) => (
{vendors.map(vendor => (
<VendorRow
key={vendor.node.id}
vendor={vendor.node}
@@ -223,7 +223,7 @@ function References({ references }: { references: Reference[] }) {
<div className="mb-8">
<h2 className="font-medium mb-4">{__("Trusted by")}</h2>
<Card className="grid grid-cols-2 flex-wrap p-6 gap-4 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-7">
{references.map((reference) => (
{references.map(reference => (
<a
key={reference.id}
href={reference.websiteUrl}

View File

@@ -13,8 +13,8 @@ type Props = {
export function SubprocessorsPage({ queryRef }: Props) {
const { __ } = useTranslate();
const data = usePreloadedQuery(currentTrustVendorsQuery, queryRef);
const vendors =
data.currentTrustCenter?.vendors.edges.map((edge) => edge.node) ?? [];
const vendors
= data.currentTrustCenter?.vendors.edges.map(edge => edge.node) ?? [];
const hasAnyCountries = vendors.some(vendor => vendor.countries.length > 0);
@@ -24,11 +24,11 @@ export function SubprocessorsPage({ queryRef }: Props) {
<p className="text-sm text-txt-secondary mb-4">
{sprintf(
__("Third-party subprocessors %s work with:"),
data.currentTrustCenter?.organization.name ?? ""
data.currentTrustCenter?.organization.name ?? "",
)}
</p>
<Rows>
{vendors.map((vendor) => (
{vendors.map(vendor => (
<VendorRow key={vendor.id} vendor={vendor} hasAnyCountries={hasAnyCountries} />
))}
</Rows>

View File

@@ -59,12 +59,11 @@ export function ConnectPage(props: {
if (!magicLinkSent && interval.current) {
clearInterval(interval.current);
interval.current = undefined;
setTimer(timerDurationSeconds);
}
if (magicLinkSent) {
clearInterval(interval.current);
interval.current = setInterval(() => {
setTimer((timer) => Math.max(timer - 1, 0));
setTimer(timer => Math.max(timer - 1, 0));
}, 1000);
}
@@ -138,7 +137,7 @@ export function ConnectPage(props: {
</p>
</div>
<form onSubmit={handleSubmit} className="space-y-4">
<form onSubmit={e => void handleSubmit(e)} className="space-y-4">
<Field
label={__("Email")}
placeholder="john.doe@acme.com"

View File

@@ -5,8 +5,8 @@ import { RelayProvider } from "/providers/RelayProviders";
import type { ConnectPageQuery } from "./__generated__/ConnectPageQuery.graphql";
function ConnectPageLoader() {
const [queryRef, loadQuery] =
useQueryLoader<ConnectPageQuery>(connectPageQuery);
const [queryRef, loadQuery]
= useQueryLoader<ConnectPageQuery>(connectPageQuery);
useEffect(() => {
if (!queryRef) {

View File

@@ -42,7 +42,7 @@ export default function VerifyMagicLinkPagePageMutation() {
verifyMagicLinkMutation,
);
const handleSubmit = form.handleSubmit(async (data) => {
const handleSubmit = form.handleSubmit((data) => {
verifyMagicLink({
variables: {
input: {
@@ -79,7 +79,7 @@ export default function VerifyMagicLinkPagePageMutation() {
useEffect(() => {
if (!submittedRef.current && searchParams.get("token")) {
handleSubmit();
void handleSubmit();
submittedRef.current = true;
}
});
@@ -94,7 +94,7 @@ export default function VerifyMagicLinkPagePageMutation() {
</p>
</div>
<form onSubmit={handleSubmit} className="space-y-4">
<form onSubmit={e => void handleSubmit(e)} className="space-y-4">
<Field
label={__("Confirmation Token")}
type="text"

View File

@@ -1,14 +1,13 @@
import {
Environment,
type FetchFunction,
Network,
RecordSource,
Store,
} from "relay-runtime";
import { GraphQLError } from "graphql";
import type { PropsWithChildren } from "react";
import { RelayEnvironmentProvider } from "react-relay";
import { getPathPrefix } from "/utils/pathPrefix";
import { makeFetchQuery } from "@probo/relay";
export class UnAuthenticatedError extends Error {
constructor() {
@@ -43,8 +42,8 @@ export function buildEndpoint(): string {
host = window.location.origin;
}
const formattedHost =
host.startsWith("http://") || host.startsWith("https://")
const formattedHost
= host.startsWith("http://") || host.startsWith("https://")
? host
: `https://${host}`;
@@ -63,99 +62,6 @@ export function buildEndpoint(): string {
return url.toString();
}
const hasUnauthenticatedError = (error: GraphQLError) =>
error.extensions?.code == "UNAUTHENTICATED";
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 {
// Extract slug from URL if present for slug-based routing
const slugMatch = window.location.pathname.match(/^\/trust\/([^/]+)/);
const slug = slugMatch ? slugMatch[1] : null;
requestInit.headers = {
Accept:
"application/graphql-response+json; charset=utf-8, application/json; charset=utf-8",
"Content-Type": "application/json",
...(slug ? { "X-Trust-Slug": slug } : {}),
};
requestInit.body = JSON.stringify({
operationName: request.name,
query: request.text,
variables,
});
}
const response = await fetch(buildEndpoint(), requestInit);
if (response.status === 500) {
throw new InternalServerError();
}
const json = await response.json();
if (json.errors) {
const errors = json.errors as GraphQLError[];
if (errors.find(hasUnauthenticatedError)) {
throw new UnAuthenticatedError();
}
const invalidError = errors.find(hasInvalidError);
if (invalidError) {
throw new InvalidError(
invalidError.message,
(invalidError.extensions.field as string) ?? "",
(invalidError.extensions.cause as string) ?? "",
);
}
throw new Error(`Error fetching GraphQL query '${request.name}'`);
}
return json;
};
const source = new RecordSource();
const store = new Store(source, {
queryCacheExpirationTime: 1 * 60 * 1000,
@@ -164,7 +70,7 @@ const store = new Store(source, {
export const consoleEnvironment = new Environment({
configName: "trust",
network: Network.create(fetchRelay),
network: Network.create(makeFetchQuery(buildEndpoint())),
store,
});

View File

@@ -27,7 +27,7 @@ import {
function ErrorBoundary({ error: propsError }: { error?: string }) {
const error = useRouteError() ?? propsError;
return <PageError error={error?.toString()} />;
return <PageError error={error instanceof Error ? error.message : ""} />;
}
const routes = [
@@ -41,7 +41,8 @@ const routes = [
},
{
path: "/",
loader: async () => {
loader: () => {
// eslint-disable-next-line
throw redirect("/overview");
},
Component: Fragment,

View File

@@ -1,7 +1,7 @@
export type NodeOf<T> =
NonNullable<T> extends
| { readonly edges: ReadonlyArray<{ readonly node: infer U }> }
| undefined
export type NodeOf<T>
= NonNullable<T> extends
| { readonly edges: ReadonlyArray<{ readonly node: infer U }> }
| undefined
? U
: never;

View File

@@ -1,6 +1,7 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"allowJs": true,
"target": "ES2022",
"lib": ["ES2023"],
"module": "ESNext",
@@ -21,5 +22,5 @@
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["vite.config.ts"]
"include": ["vite.config.ts", "eslint.config.mjs"]
}