Fix some unexpected any

Signed-off-by: Émile Ré <nemile.re@gmail.com>
This commit is contained in:
Émile Ré
2025-12-03 16:03:24 +04:00
parent c2d3a38057
commit e2ef4b005d
7 changed files with 68 additions and 101 deletions

View File

@@ -94,7 +94,7 @@ export function TrustCenterAuditsCard<Params>(props: Props<Params>) {
{audits.map((audit, index) => ( {audits.map((audit, index) => (
<AuditRow <AuditRow
key={index} key={index}
audit={audit} auditFragmentRef={audit}
onChangeVisibility={onChangeVisibility} onChangeVisibility={onChangeVisibility}
disabled={props.disabled} disabled={props.disabled}
/> />
@@ -116,23 +116,24 @@ export function TrustCenterAuditsCard<Params>(props: Props<Params>) {
} }
function AuditRow(props: { function AuditRow(props: {
audit: TrustCenterAuditsCardFragment$key; auditFragmentRef: TrustCenterAuditsCardFragment$key;
onChangeVisibility: (auditId: string, trustCenterVisibility: "NONE" | "PRIVATE" | "PUBLIC") => void; onChangeVisibility: (auditId: string, trustCenterVisibility: "NONE" | "PRIVATE" | "PUBLIC") => void;
disabled?: boolean; disabled?: boolean;
}) { }) {
const audit = useFragment(trustCenterAuditFragment, props.audit); const { auditFragmentRef, onChangeVisibility, disabled } = props;
const audit = useFragment(trustCenterAuditFragment, auditFragmentRef);
const organizationId = useOrganizationId(); const organizationId = useOrganizationId();
const { __ } = useTranslate(); const { __ } = useTranslate();
const [optimisticValue, setOptimisticValue] = useState<string | null>(null); const [optimisticValue, setOptimisticValue] = useState<string | null>(null);
const { isAuthorized } = use(PermissionsContext); const { isAuthorized } = use(PermissionsContext);
const canUpdate = organizationId ? isAuthorized("TrustCenter", "updateTrustCenter") : false; const canUpdate = organizationId ? isAuthorized("TrustCenter", "updateTrustCenter") : false;
const handleValueChange = useCallback((value: string | {}) => { const handleValueChange = useCallback((value: string) => {
const stringValue = typeof value === 'string' ? value : ''; const stringValue = typeof value === 'string' ? value : '';
const typedValue = stringValue as "NONE" | "PRIVATE" | "PUBLIC"; const typedValue = stringValue as "NONE" | "PRIVATE" | "PUBLIC";
setOptimisticValue(typedValue); setOptimisticValue(typedValue);
props.onChangeVisibility(audit.id, typedValue); onChangeVisibility(audit.id, typedValue);
}, [audit.id, props.onChangeVisibility]); }, [audit.id, onChangeVisibility]);
useEffect(() => { useEffect(() => {
if (optimisticValue && audit.trustCenterVisibility === optimisticValue) { if (optimisticValue && audit.trustCenterVisibility === optimisticValue) {
@@ -167,7 +168,7 @@ function AuditRow(props: {
type="select" type="select"
value={currentValue} value={currentValue}
onValueChange={handleValueChange} onValueChange={handleValueChange}
disabled={props.disabled || !canUpdate} disabled={disabled || !canUpdate}
className="w-[105px]" className="w-[105px]"
> >
{visibilityOptions.map((option) => ( {visibilityOptions.map((option) => (

View File

@@ -98,7 +98,7 @@ export function TrustCenterDocumentsCard<Params>(props: Props<Params>) {
{documents.map((document, index) => ( {documents.map((document, index) => (
<DocumentRow <DocumentRow
key={index} key={index}
document={document} documentFragmentRef={document}
onChangeVisibility={onChangeVisibility} onChangeVisibility={onChangeVisibility}
disabled={props.disabled} disabled={props.disabled}
/> />
@@ -121,23 +121,24 @@ export function TrustCenterDocumentsCard<Params>(props: Props<Params>) {
} }
function DocumentRow(props: { function DocumentRow(props: {
document: TrustCenterDocumentsCardFragment$key; documentFragmentRef: TrustCenterDocumentsCardFragment$key;
onChangeVisibility: (documentId: string, trustCenterVisibility: "NONE" | "PRIVATE" | "PUBLIC") => void; onChangeVisibility: (documentId: string, trustCenterVisibility: "NONE" | "PRIVATE" | "PUBLIC") => void;
disabled?: boolean; disabled?: boolean;
}) { }) {
const document = useFragment(trustCenterDocumentFragment, props.document); const { documentFragmentRef, onChangeVisibility, disabled } = props;
const document = useFragment(trustCenterDocumentFragment, documentFragmentRef);
const organizationId = useOrganizationId(); const organizationId = useOrganizationId();
const { __ } = useTranslate(); const { __ } = useTranslate();
const [optimisticValue, setOptimisticValue] = useState<string | null>(null); const [optimisticValue, setOptimisticValue] = useState<string | null>(null);
const { isAuthorized } = use(PermissionsContext); const { isAuthorized } = use(PermissionsContext);
const canUpdate = isAuthorized("TrustCenter", "updateTrustCenter"); const canUpdate = isAuthorized("TrustCenter", "updateTrustCenter");
const handleValueChange = useCallback((value: string | {}) => { const handleValueChange = useCallback((value: string) => {
const stringValue = typeof value === 'string' ? value : ''; const stringValue = typeof value === 'string' ? value : '';
const typedValue = stringValue as "NONE" | "PRIVATE" | "PUBLIC"; const typedValue = stringValue as "NONE" | "PRIVATE" | "PUBLIC";
setOptimisticValue(typedValue); setOptimisticValue(typedValue);
props.onChangeVisibility(document.id, typedValue); onChangeVisibility(document.id, typedValue);
}, [document.id, props.onChangeVisibility]); }, [document.id, onChangeVisibility]);
useEffect(() => { useEffect(() => {
if (optimisticValue && document.trustCenterVisibility === optimisticValue) { if (optimisticValue && document.trustCenterVisibility === optimisticValue) {
@@ -167,7 +168,7 @@ function DocumentRow(props: {
type="select" type="select"
value={currentValue} value={currentValue}
onValueChange={handleValueChange} onValueChange={handleValueChange}
disabled={props.disabled || !canUpdate} disabled={disabled || !canUpdate}
className="w-[105px]" className="w-[105px]"
> >
{visibilityOptions.map((option) => ( {visibilityOptions.map((option) => (

View File

@@ -145,18 +145,18 @@ function FileRow(props: {
onDelete: (id: string) => void; onDelete: (id: string) => void;
disabled?: boolean; disabled?: boolean;
}) { }) {
const file = props.file; const { file, onChangeVisibility, onEdit, onDelete, disabled } = props;
const { __ } = useTranslate(); const { __ } = useTranslate();
const [optimisticValue, setOptimisticValue] = useState<string | null>(null); const [optimisticValue, setOptimisticValue] = useState<string | null>(null);
const { isAuthorized } = use(PermissionsContext); const { isAuthorized } = use(PermissionsContext);
const canUpdate = isAuthorized("TrustCenter", "updateTrustCenter"); const canUpdate = isAuthorized("TrustCenter", "updateTrustCenter");
const handleValueChange = useCallback((value: string | {}) => { const handleValueChange = useCallback((value: string) => {
const stringValue = typeof value === 'string' ? value : ''; const stringValue = typeof value === 'string' ? value : '';
const typedValue = stringValue as "NONE" | "PRIVATE" | "PUBLIC"; const typedValue = stringValue as "NONE" | "PRIVATE" | "PUBLIC";
setOptimisticValue(typedValue); setOptimisticValue(typedValue);
props.onChangeVisibility(file.id, typedValue); onChangeVisibility(file.id, typedValue);
}, [file.id, props.onChangeVisibility]); }, [file.id, onChangeVisibility]);
useEffect(() => { useEffect(() => {
if (optimisticValue && file.trustCenterVisibility === optimisticValue) { if (optimisticValue && file.trustCenterVisibility === optimisticValue) {
@@ -182,7 +182,7 @@ function FileRow(props: {
type="select" type="select"
value={currentValue} value={currentValue}
onValueChange={handleValueChange} onValueChange={handleValueChange}
disabled={props.disabled || !canUpdate} disabled={disabled || !canUpdate}
className="w-[105px]" className="w-[105px]"
> >
{visibilityOptions.map((option) => ( {visibilityOptions.map((option) => (
@@ -208,8 +208,8 @@ function FileRow(props: {
<Button <Button
variant="secondary" variant="secondary"
icon={IconPencil} icon={IconPencil}
onClick={() => props.onEdit({ id: file.id, name: file.name, category: file.category })} onClick={() => onEdit({ id: file.id, name: file.name, category: file.category })}
disabled={props.disabled} disabled={disabled}
title={__("Edit")} title={__("Edit")}
/> />
)} )}
@@ -217,8 +217,8 @@ function FileRow(props: {
<Button <Button
variant="danger" variant="danger"
icon={IconTrashCan} icon={IconTrashCan}
onClick={() => props.onDelete(file.id)} onClick={() => onDelete(file.id)}
disabled={props.disabled} disabled={disabled}
title={__("Delete")} title={__("Delete")}
/> />
)} )}

View File

@@ -1,8 +1,9 @@
import { graphql } from 'react-relay'; import { graphql } from 'react-relay';
import { useLazyLoadQuery, usePaginationFragment } from 'react-relay'; import { useLazyLoadQuery, usePaginationFragment } from 'react-relay';
import type { import type {
TrustCenterAccessGraphQuery TrustCenterAccessGraphQuery,
} from "./__generated__/TrustCenterAccessGraphQuery.graphql"; } from "./__generated__/TrustCenterAccessGraphQuery.graphql";
import type { TrustCenterAccessGraph_accesses$data, TrustCenterAccessGraph_accesses$key } from './__generated__/TrustCenterAccessGraph_accesses.graphql';
export const trustCenterAccessesPaginationFragment = graphql` export const trustCenterAccessesPaginationFragment = graphql`
fragment TrustCenterAccessGraph_accesses on TrustCenter fragment TrustCenterAccessGraph_accesses on TrustCenter
@@ -140,7 +141,7 @@ export const loadTrustCenterAccessDocumentAccessesQuery = graphql`
`; `;
interface PaginatedData { interface PaginatedData {
data: { node: any } | null; data: TrustCenterAccessGraph_accesses$data | null;
hasNext: boolean; hasNext: boolean;
loadMore: () => void; loadMore: () => void;
isLoadingNext: boolean; isLoadingNext: boolean;
@@ -157,6 +158,18 @@ export function useTrustCenterAccesses(trustCenterId: string): PaginatedData {
{ fetchPolicy: 'store-and-network' } { fetchPolicy: 'store-and-network' }
); );
const trustCenter = data?.node;
const {
data: paginationData,
loadNext,
hasNext,
isLoadingNext,
} = usePaginationFragment<TrustCenterAccessGraphQuery, TrustCenterAccessGraph_accesses$key>(
trustCenterAccessesPaginationFragment,
trustCenter
);
if (!trustCenterId) { if (!trustCenterId) {
return { return {
data: null, data: null,
@@ -166,24 +179,12 @@ export function useTrustCenterAccesses(trustCenterId: string): PaginatedData {
}; };
} }
const trustCenter = data?.node as any;
const {
data: paginationData,
loadNext,
hasNext,
isLoadingNext,
} = usePaginationFragment(
trustCenterAccessesPaginationFragment,
trustCenter
);
const loadMore = () => { const loadMore = () => {
loadNext(10); loadNext(10);
}; };
return { return {
data: { node: paginationData }, data: paginationData,
hasNext, hasNext,
loadMore, loadMore,
isLoadingNext, isLoadingNext,

View File

@@ -24,8 +24,8 @@ import {
import { useTranslate } from "@probo/i18n"; import { useTranslate } from "@probo/i18n";
import { formatDate } from "@probo/helpers"; import { formatDate } from "@probo/helpers";
import { useOutletContext } from "react-router"; import { useOutletContext } from "react-router";
import { useState, useCallback, useEffect, useRef, use } from "react"; import { useState, useCallback, useEffect, useRef, use, useMemo } from "react";
import { useQueryLoader, usePreloadedQuery } from 'react-relay'; import { useQueryLoader, usePreloadedQuery, type PreloadedQuery } from 'react-relay';
import z from "zod"; import z from "zod";
import { import {
useTrustCenterAccesses, useTrustCenterAccesses,
@@ -37,6 +37,7 @@ import {
import { useFormWithSchema } from "/hooks/useFormWithSchema"; import { useFormWithSchema } from "/hooks/useFormWithSchema";
import { useMutationWithToasts } from "/hooks/useMutationWithToasts"; import { useMutationWithToasts } from "/hooks/useMutationWithToasts";
import { PermissionsContext } from "/providers/PermissionsContext"; import { PermissionsContext } from "/providers/PermissionsContext";
import type { TrustCenterAccessGraphLoadDocumentAccessesQuery } from "/hooks/graph/__generated__/TrustCenterAccessGraphLoadDocumentAccessesQuery.graphql";
type ContextType = { type ContextType = {
organization: { organization: {
@@ -80,7 +81,6 @@ type ContextType = {
}; };
type DocumentAccessInfo = { type DocumentAccessInfo = {
id: string;
active: boolean; active: boolean;
requested: boolean; requested: boolean;
document?: { document?: {
@@ -91,12 +91,12 @@ type DocumentAccessInfo = {
report?: { report?: {
id: string; id: string;
filename: string; filename: string;
audit: { audit?: {
id: string; id: string;
framework: { framework: {
name: string; name: string;
}; };
}; } | null;
} | null; } | null;
trustCenterFile?: { trustCenterFile?: {
id: string; id: string;
@@ -109,16 +109,16 @@ function DocumentAccessesLoader({
queryReference, queryReference,
onDataLoaded onDataLoaded
}: { }: {
queryReference: any; queryReference: PreloadedQuery<TrustCenterAccessGraphLoadDocumentAccessesQuery>;
onDataLoaded: (documentAccesses: DocumentAccessInfo[]) => void; onDataLoaded: (documentAccesses: DocumentAccessInfo[]) => void;
}) { }) {
const data = usePreloadedQuery(loadTrustCenterAccessDocumentAccessesQuery, queryReference); const data = usePreloadedQuery(loadTrustCenterAccessDocumentAccessesQuery, queryReference);
useEffect(() => { useEffect(() => {
if (data && typeof data === 'object' && 'node' in data) { if (data && typeof data === 'object' && 'node' in data) {
const node = (data as any).node; const node = data.node;
if (node?.availableDocumentAccesses?.edges) { if (node?.availableDocumentAccesses?.edges) {
const documentAccesses = node.availableDocumentAccesses.edges.map((edge: any) => edge.node); const documentAccesses: DocumentAccessInfo[] = node.availableDocumentAccesses.edges.map((edge) => edge.node);
onDataLoaded(documentAccesses); onDataLoaded(documentAccesses);
} }
} }
@@ -157,10 +157,10 @@ export default function TrustCenterAccessTab() {
const dialogRef = useDialogRef(); const dialogRef = useDialogRef();
const editDialogRef = useDialogRef(); const editDialogRef = useDialogRef();
const [editingAccess, setEditingAccess] = useState<AccessType | null>(null); const [editingAccess, setEditingAccess] = useState<AccessType | null>(null);
const [editingDocumentAccesses, setEditingDocumentAccesses] = useState<DocumentAccessType[]>([]); const [editingDocumentAccesses, setEditingDocumentAccesses] = useState<DocumentAccessInfo[]>([]);
const [selectedDocumentAccesses, setSelectedDocumentAccesses] = useState<Set<string>>(new Set()); const [selectedDocumentAccesses, setSelectedDocumentAccesses] = useState<Set<string>>(new Set());
const [pendingEditEmail, setPendingEditEmail] = useState<string | null>(null); const [pendingEditEmail, setPendingEditEmail] = useState<string | null>(null);
const [documentAccessesQueryReference, loadDocumentAccessesQuery] = useQueryLoader(loadTrustCenterAccessDocumentAccessesQuery); const [documentAccessesQueryReference, loadDocumentAccessesQuery] = useQueryLoader<TrustCenterAccessGraphLoadDocumentAccessesQuery>(loadTrustCenterAccessDocumentAccessesQuery);
const loadedAccessIdRef = useRef<string | null>(null); const loadedAccessIdRef = useRef<string | null>(null);
const [isLoadingDocumentAccesses, setIsLoadingDocumentAccesses] = useState(false); const [isLoadingDocumentAccesses, setIsLoadingDocumentAccesses] = useState(false);
@@ -197,37 +197,11 @@ export default function TrustCenterAccessTab() {
defaultValues: { name: "", active: false }, defaultValues: { name: "", active: false },
}); });
type DocumentAccessType = {
id: string;
active: boolean;
requested: boolean;
document?: {
id: string;
title: string;
documentType: string;
} | null;
report?: {
id: string;
filename: string;
audit: {
id: string;
framework: {
name: string;
};
};
} | null;
trustCenterFile?: {
id: string;
name: string;
category: string;
} | null;
};
function getDocumentAccessInfo( function getDocumentAccessInfo(
docAccess: DocumentAccessType, docAccess: DocumentAccessInfo,
__: (key: string) => string __: (key: string) => string
) { ) {
if (!!docAccess.document) { if (docAccess.document) {
return { return {
variant: "info" as const, variant: "info" as const,
name: docAccess.document?.title, name: docAccess.document?.title,
@@ -238,7 +212,7 @@ export default function TrustCenterAccessTab() {
active: docAccess.active, active: docAccess.active,
}; };
} }
if (!!docAccess.report) { if (docAccess.report) {
return { return {
variant: "success" as const, variant: "success" as const,
name: docAccess.report?.filename, name: docAccess.report?.filename,
@@ -249,7 +223,7 @@ export default function TrustCenterAccessTab() {
active: docAccess.active, active: docAccess.active,
}; };
} }
if (!!docAccess.trustCenterFile) { if (docAccess.trustCenterFile) {
return { return {
variant: "highlight" as const, variant: "highlight" as const,
name: docAccess.trustCenterFile?.name, name: docAccess.trustCenterFile?.name,
@@ -274,30 +248,21 @@ export default function TrustCenterAccessTab() {
lastTokenExpiresAt: string | null; lastTokenExpiresAt: string | null;
pendingRequestCount: number; pendingRequestCount: number;
activeCount: number; activeCount: number;
documentAccesses?: DocumentAccessType[]; documentAccesses?: DocumentAccessInfo[];
}; };
const { data: trustCenterData, loadMore, hasNext, isLoadingNext } = useTrustCenterAccesses(organization.trustCenter?.id || ""); const { data: trustCenterData, loadMore, hasNext, isLoadingNext } = useTrustCenterAccesses(organization.trustCenter?.id || "");
const accesses: AccessType[] = trustCenterData?.node?.accesses?.edges?.map((edge: any) => ({ const accesses: AccessType[] = useMemo(
id: edge.node.id, () => trustCenterData?.accesses?.edges.map((edge) => edge.node) ?? [], [trustCenterData?.accesses?.edges]
email: edge.node.email, );
name: edge.node.name,
active: edge.node.active,
hasAcceptedNonDisclosureAgreement: edge.node.hasAcceptedNonDisclosureAgreement,
createdAt: edge.node.createdAt,
lastTokenExpiresAt: edge.node.lastTokenExpiresAt,
pendingRequestCount: edge.node.pendingRequestCount || 0,
activeCount: edge.node.activeCount || 0,
})) ?? [];
const handleInvite = inviteForm.handleSubmit(async (data) => { const handleInvite = inviteForm.handleSubmit(async (data) => {
if (!organization.trustCenter?.id) { if (!organization.trustCenter?.id) {
return; return;
} }
const connectionId = trustCenterData?.node?.accesses?.__id; const connectionId = trustCenterData?.accesses?.__id;
const email = data.email.trim(); const email = data.email.trim();
await createInvitation({ await createInvitation({
@@ -317,7 +282,7 @@ export default function TrustCenterAccessTab() {
}); });
const handleDelete = useCallback(async (id: string) => { const handleDelete = useCallback(async (id: string) => {
const connectionId = trustCenterData?.node?.accesses?.__id; const connectionId = trustCenterData?.accesses?.__id;
await deleteInvitation({ await deleteInvitation({
variables: { variables: {

View File

@@ -8,14 +8,13 @@ import { input } from "../Input/Input";
import clsx from "clsx"; import clsx from "clsx";
type Props = TextareaHTMLAttributes<HTMLTextAreaElement> & { type Props = TextareaHTMLAttributes<HTMLTextAreaElement> & {
variant?: "bordered" | "ghost" | "title";
autogrow?: boolean; autogrow?: boolean;
ref?: RefCallback<HTMLTextAreaElement>; ref?: RefCallback<HTMLTextAreaElement>;
}; };
export function Textarea(props: Props) { export function Textarea(props: Props) {
const ref = useRef<HTMLTextAreaElement>(null); const ref = useRef<HTMLTextAreaElement>(null);
const { autogrow, variant, ref: propsRef, ...restProps } = props; const { autogrow, ref: propsRef, ...restProps } = props;
const adjustHeight = () => { const adjustHeight = () => {
if (!autogrow || !ref.current) return; if (!autogrow || !ref.current) return;

View File

@@ -14,7 +14,7 @@ type BaseProps<T extends string, P> = {
children?: ReactNode; children?: ReactNode;
} & P; } & P;
type Props = type Props<T extends string | readonly string[] | number = string> =
| BaseProps<never, ComponentProps<typeof Input>> | BaseProps<never, ComponentProps<typeof Input>>
| BaseProps<"text", ComponentProps<typeof Input>> | BaseProps<"text", ComponentProps<typeof Input>>
| BaseProps<"email", ComponentProps<typeof Input>> | BaseProps<"email", ComponentProps<typeof Input>>
@@ -22,7 +22,7 @@ type Props =
| BaseProps<"password", ComponentProps<typeof Input>> | BaseProps<"password", ComponentProps<typeof Input>>
| BaseProps<"textarea", ComponentProps<typeof Textarea>> | BaseProps<"textarea", ComponentProps<typeof Textarea>>
| BaseProps<"number", ComponentProps<typeof Input>> | BaseProps<"number", ComponentProps<typeof Input>>
| BaseProps<"select", ComponentProps<typeof Select>>; | BaseProps<"select", ComponentProps<typeof Select<T>>>;
const field = tv({ const field = tv({
slots: { slots: {
@@ -34,7 +34,7 @@ const field = tv({
const { base: baseClass, label: labelClass, help: helpClass } = field(); const { base: baseClass, label: labelClass, help: helpClass } = field();
export function Field(props: Props) { export function Field<T extends string | readonly string[] | number = string>(props: Props<T>) {
const showHelp = props.help && !props.error; const showHelp = props.help && !props.error;
const childrenAsInput = !props.type && props.children; const childrenAsInput = !props.type && props.children;
return ( return (
@@ -55,8 +55,8 @@ export function Field(props: Props) {
); );
} }
function getInput(props: Props) { function getInput<T extends string | readonly string[] | number = string>(props: Props<T>) {
const { label, error, onValueChange, type, ...restProps } = props; const { error, onValueChange, type, ...restProps } = props;
const baseProps = { const baseProps = {
["aria-invalid"]: !!error, ["aria-invalid"]: !!error,
name: props.name, name: props.name,
@@ -67,7 +67,7 @@ function getInput(props: Props) {
case "select": case "select":
return ( return (
// @ts-expect-error Select is too dynamic // @ts-expect-error Select is too dynamic
<Select <Select<T>
{...baseProps} {...baseProps}
{...restProps} {...restProps}
onValueChange={onValueChange} onValueChange={onValueChange}