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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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