Fix any type usage in proprietary/ (#5949)

# Description of Changes
Follow on from #5934, expanding `any` type usage ban to the
`proprietary/` folder
This commit is contained in:
James Brunton
2026-04-01 08:21:26 +00:00
committed by GitHub
parent a96b95e198
commit c31e4253dd
34 changed files with 341 additions and 266 deletions
+4 -1
View File
@@ -83,7 +83,10 @@ export default defineConfig(
}, },
// Folders that have been cleaned up and are now conformant - stricter rules enforced here // Folders that have been cleaned up and are now conformant - stricter rules enforced here
{ {
files: ['src/saas/**/*.{js,mjs,jsx,ts,tsx}'], files: [
'src/proprietary/**/*.{js,mjs,jsx,ts,tsx}',
'src/saas/**/*.{js,mjs,jsx,ts,tsx}',
],
languageOptions: { languageOptions: {
parserOptions: { parserOptions: {
project: true, project: true,
@@ -2,7 +2,7 @@ import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
import { springAuth } from '@app/auth/springAuthClient'; import { springAuth } from '@app/auth/springAuthClient';
import { startOAuthNavigation } from '@app/extensions/oauthNavigation'; import { startOAuthNavigation } from '@app/extensions/oauthNavigation';
import apiClient from '@app/services/apiClient'; import apiClient from '@app/services/apiClient';
import { AxiosError } from 'axios'; import { AxiosError, type AxiosResponse, type InternalAxiosRequestConfig } from 'axios';
// Mock apiClient // Mock apiClient
vi.mock('@app/services/apiClient'); vi.mock('@app/services/apiClient');
@@ -45,7 +45,7 @@ describe('SpringAuthClient', () => {
vi.mocked(apiClient.get).mockResolvedValueOnce({ vi.mocked(apiClient.get).mockResolvedValueOnce({
status: 200, status: 200,
data: { user: mockUser }, data: { user: mockUser },
} as any); } as unknown as AxiosResponse);
const result = await springAuth.getSession(); const result = await springAuth.getSession();
@@ -74,7 +74,7 @@ describe('SpringAuthClient', () => {
statusText: 'Unauthorized', statusText: 'Unauthorized',
data: {}, data: {},
headers: {}, headers: {},
config: {} as any, config: {} as InternalAxiosRequestConfig,
} }
); );
@@ -102,7 +102,7 @@ describe('SpringAuthClient', () => {
statusText: 'Forbidden', statusText: 'Forbidden',
data: {}, data: {},
headers: {}, headers: {},
config: {} as any, config: {} as InternalAxiosRequestConfig,
} }
); );
@@ -141,7 +141,7 @@ describe('SpringAuthClient', () => {
expires_in: 3600, expires_in: 3600,
}, },
}, },
} as any); } as unknown as AxiosResponse);
// Spy on window.dispatchEvent // Spy on window.dispatchEvent
const dispatchEventSpy = vi.spyOn(window, 'dispatchEvent'); const dispatchEventSpy = vi.spyOn(window, 'dispatchEvent');
@@ -208,7 +208,7 @@ describe('SpringAuthClient', () => {
vi.mocked(apiClient.post).mockResolvedValueOnce({ vi.mocked(apiClient.post).mockResolvedValueOnce({
status: 200, status: 200,
data: { user: mockUser }, data: { user: mockUser },
} as any); } as unknown as AxiosResponse);
const result = await springAuth.signUp(credentials); const result = await springAuth.signUp(credentials);
@@ -259,7 +259,7 @@ describe('SpringAuthClient', () => {
vi.mocked(apiClient.post).mockResolvedValueOnce({ vi.mocked(apiClient.post).mockResolvedValueOnce({
status: 200, status: 200,
data: {}, data: {},
} as any); } as unknown as AxiosResponse);
const result = await springAuth.signOut(); const result = await springAuth.signOut();
@@ -308,7 +308,7 @@ describe('SpringAuthClient', () => {
expires_in: 3600, expires_in: 3600,
}, },
}, },
} as any); } as unknown as AxiosResponse);
const result = await springAuth.refreshSession(); const result = await springAuth.refreshSession();
@@ -85,7 +85,7 @@ export interface User {
is_anonymous?: boolean; is_anonymous?: boolean;
isFirstLogin?: boolean; isFirstLogin?: boolean;
authenticationType?: string; authenticationType?: string;
app_metadata?: Record<string, any>; app_metadata?: Record<string, unknown>;
} }
export interface Session { export interface Session {
@@ -447,7 +447,7 @@ class SpringAuthClient {
*/ */
async signInWithOAuth(params: { async signInWithOAuth(params: {
provider: OAuthProvider; provider: OAuthProvider;
options?: { redirectTo?: string; queryParams?: Record<string, any> }; options?: { redirectTo?: string; queryParams?: Record<string, string> };
}): Promise<{ error: AuthError | null }> { }): Promise<{ error: AuthError | null }> {
try { try {
const redirectPath = normalizeRedirectPath(params.options?.redirectTo); const redirectPath = normalizeRedirectPath(params.options?.redirectTo);
@@ -1,4 +1,5 @@
import { useEffect, useMemo, useState } from 'react'; import { useEffect, useMemo, useState } from 'react';
import { isAxiosError } from 'axios';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { import {
ActionIcon, ActionIcon,
@@ -125,8 +126,10 @@ export default function ChangeUserPasswordModal({ opened, onClose, user, onSucce
alert({ alertType: 'success', title: t('workspace.people.changePassword.success', 'Password updated successfully') }); alert({ alertType: 'success', title: t('workspace.people.changePassword.success', 'Password updated successfully') });
onSuccess(); onSuccess();
handleClose(); handleClose();
} catch (error: any) { } catch (error: unknown) {
const errorMessage = error.response?.data?.message || error.response?.data?.error || error.message || t('workspace.people.changePassword.error', 'Failed to update password'); const errorMessage = isAxiosError(error)
? (error.response?.data?.message || error.response?.data?.error || error.message)
: (error instanceof Error ? error.message : undefined) || t('workspace.people.changePassword.error', 'Failed to update password');
alert({ alertType: 'error', title: errorMessage }); alert({ alertType: 'error', title: errorMessage });
} finally { } finally {
setProcessing(false); setProcessing(false);
@@ -12,7 +12,7 @@ interface TextDividerProps {
export default function DividerWithText({ text, className = '', style, variant = 'default', respondsToDarkMode = true, opacity }: TextDividerProps) { export default function DividerWithText({ text, className = '', style, variant = 'default', respondsToDarkMode = true, opacity }: TextDividerProps) {
const variantClass = variant === 'subcategory' ? 'subcategory' : ''; const variantClass = variant === 'subcategory' ? 'subcategory' : '';
const themeClass = respondsToDarkMode ? '' : 'force-light'; const themeClass = respondsToDarkMode ? '' : 'force-light';
const styleWithOpacity = opacity !== undefined ? { ...(style || {}), ['--text-divider-opacity' as any]: opacity } : style; const styleWithOpacity = opacity !== undefined ? { ...(style || {}), ['--text-divider-opacity' as string]: opacity } : style;
if (text) { if (text) {
return ( return (
@@ -1,4 +1,5 @@
import { useState, useEffect, useRef } from 'react'; import { useState, useEffect, useRef } from 'react';
import { isAxiosError } from 'axios';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { import {
Modal, Modal,
@@ -158,9 +159,11 @@ export default function InviteMembersModal({ opened, onClose, onSuccess }: Invit
forceChange: false, forceChange: false,
forceMFA: false, forceMFA: false,
}); });
} catch (error: any) { } catch (error: unknown) {
console.error('Failed to invite user:', error); console.error('Failed to invite user:', error);
const errorMessage = error.response?.data?.message || error.response?.data?.error || error.message || t('workspace.people.addMember.error'); const errorMessage = isAxiosError(error)
? (error.response?.data?.message || error.response?.data?.error || error.message)
: (error instanceof Error ? error.message : undefined) || t('workspace.people.addMember.error');
alert({ alertType: 'error', title: errorMessage }); alert({ alertType: 'error', title: errorMessage });
} finally { } finally {
setProcessing(false); setProcessing(false);
@@ -211,12 +214,11 @@ export default function InviteMembersModal({ opened, onClose, onSuccess }: Invit
body: response.errors || response.error body: response.errors || response.error
}); });
} }
} catch (error: any) { } catch (error: unknown) {
console.error('Failed to invite users:', error); console.error('Failed to invite users:', error);
const errorMessage = error.response?.data?.message || const errorMessage = isAxiosError(error)
error.response?.data?.error || ? (error.response?.data?.message || error.response?.data?.error || error.message)
error.message || : (error instanceof Error ? error.message : undefined) || t('workspace.people.emailInvite.error', 'Failed to send invites');
t('workspace.people.emailInvite.error', 'Failed to send invites');
alert({ alertType: 'error', title: errorMessage }); alert({ alertType: 'error', title: errorMessage });
} finally { } finally {
setProcessing(false); setProcessing(false);
@@ -239,9 +241,11 @@ export default function InviteMembersModal({ opened, onClose, onSuccess }: Invit
if (inviteLinkForm.sendEmail && inviteLinkForm.email) { if (inviteLinkForm.sendEmail && inviteLinkForm.email) {
alert({ alertType: 'success', title: t('workspace.people.inviteLink.emailSent', 'Invite link generated and sent via email') }); alert({ alertType: 'success', title: t('workspace.people.inviteLink.emailSent', 'Invite link generated and sent via email') });
} }
} catch (error: any) { } catch (error: unknown) {
console.error('Failed to generate invite link:', error); console.error('Failed to generate invite link:', error);
const errorMessage = error.response?.data?.message || error.response?.data?.error || error.message || t('workspace.people.inviteLink.error', 'Failed to generate invite link'); const errorMessage = isAxiosError(error)
? (error.response?.data?.message || error.response?.data?.error || error.message)
: (error instanceof Error ? error.message : undefined) || t('workspace.people.inviteLink.error', 'Failed to generate invite link');
alert({ alertType: 'error', title: errorMessage }); alert({ alertType: 'error', title: errorMessage });
} finally { } finally {
setProcessing(false); setProcessing(false);
@@ -34,12 +34,12 @@ export const ManageBillingButton: React.FC<ManageBillingButtonProps> = ({
// Open billing portal in new tab // Open billing portal in new tab
window.open(response.url, '_blank'); window.open(response.url, '_blank');
setLoading(false); setLoading(false);
} catch (error: any) { } catch (error: unknown) {
console.error('Failed to open billing portal:', error); console.error('Failed to open billing portal:', error);
alert({ alert({
alertType: 'error', alertType: 'error',
title: t('billing.portal.error', 'Failed to open billing portal'), title: t('billing.portal.error', 'Failed to open billing portal'),
body: error.message || 'Please try again or contact support.', body: (error instanceof Error ? error.message : undefined) || 'Please try again or contact support.',
}); });
setLoading(false); setLoading(false);
} }
@@ -86,7 +86,7 @@ const UpgradeBanner: React.FC = () => {
const scenarioProvidesInfo = const scenarioProvidesInfo =
scenarioKey && scenarioKey !== 'unknown' && scenarioKey !== 'licensed'; scenarioKey && scenarioKey !== 'unknown' && scenarioKey !== 'licensed';
const derivedIsAdmin = scenarioProvidesInfo const derivedIsAdmin = scenarioProvidesInfo
? scenarioKey!.includes('admin') ? scenarioKey.includes('admin')
: isAdmin; : isAdmin;
const derivedHasPaidLicense = const derivedHasPaidLicense =
scenarioKey === 'licensed' scenarioKey === 'licensed'
@@ -95,10 +95,10 @@ const UpgradeBanner: React.FC = () => {
? hasPaidLicense ? hasPaidLicense
: false; : false;
const derivedIsUnderLimit = scenarioProvidesInfo const derivedIsUnderLimit = scenarioProvidesInfo
? scenarioKey!.includes('under-limit') ? scenarioKey.includes('under-limit')
: isUnderLimit === true; : isUnderLimit === true;
const derivedIsOverLimit = scenarioProvidesInfo const derivedIsOverLimit = scenarioProvidesInfo
? scenarioKey!.includes('over-limit') ? scenarioKey.includes('over-limit')
: isOverLimit === true; : isOverLimit === true;
const effectiveIsAdmin = scenario const effectiveIsAdmin = scenario
@@ -1,4 +1,5 @@
import { useCallback, useEffect, useMemo, useState } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react';
import { isAxiosError } from 'axios';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { NumberInput, Switch, Button, Stack, Paper, Text, Loader, Group, Accordion, TextInput, MultiSelect } from '@mantine/core'; import { NumberInput, Switch, Button, Stack, Paper, Text, Loader, Group, Accordion, TextInput, MultiSelect } from '@mantine/core';
import { alert } from '@app/components/toast'; import { alert } from '@app/components/toast';
@@ -72,7 +73,7 @@ export default function AdminAdvancedSection() {
isFieldPending, isFieldPending,
} = useAdminSettings<AdvancedSettingsData>({ } = useAdminSettings<AdvancedSettingsData>({
sectionName: 'advanced', sectionName: 'advanced',
fetchTransformer: async (): Promise<AdvancedSettingsData & { _pending?: Record<string, any> }> => { fetchTransformer: async (): Promise<AdvancedSettingsData & { _pending?: Record<string, unknown> }> => {
const [systemResponse, processExecutorResponse] = await Promise.all([ const [systemResponse, processExecutorResponse] = await Promise.all([
apiClient.get('/api/v1/admin/settings/section/system'), apiClient.get('/api/v1/admin/settings/section/system'),
apiClient.get('/api/v1/admin/settings/section/processExecutor') apiClient.get('/api/v1/admin/settings/section/processExecutor')
@@ -81,7 +82,7 @@ export default function AdminAdvancedSection() {
const systemData = systemResponse.data || {}; const systemData = systemResponse.data || {};
const processExecutorData = processExecutorResponse.data || {}; const processExecutorData = processExecutorResponse.data || {};
const result: AdvancedSettingsData & { _pending?: Record<string, any> } = { const result: AdvancedSettingsData & { _pending?: Record<string, unknown> } = {
enableAlphaFunctionality: systemData.enableAlphaFunctionality || false, enableAlphaFunctionality: systemData.enableAlphaFunctionality || false,
maxDPI: systemData.maxDPI || 0, maxDPI: systemData.maxDPI || 0,
enableUrlToPDF: systemData.enableUrlToPDF || false, enableUrlToPDF: systemData.enableUrlToPDF || false,
@@ -101,7 +102,7 @@ export default function AdminAdvancedSection() {
}; };
// Merge pending blocks from both endpoints // Merge pending blocks from both endpoints
const pendingBlock: Record<string, any> = {}; const pendingBlock: Record<string, unknown> = {};
if (systemData._pending?.enableAlphaFunctionality !== undefined) { if (systemData._pending?.enableAlphaFunctionality !== undefined) {
pendingBlock.enableAlphaFunctionality = systemData._pending.enableAlphaFunctionality; pendingBlock.enableAlphaFunctionality = systemData._pending.enableAlphaFunctionality;
} }
@@ -131,7 +132,7 @@ export default function AdminAdvancedSection() {
return result; return result;
}, },
saveTransformer: (settings) => { saveTransformer: (settings) => {
const deltaSettings: Record<string, any> = { const deltaSettings: Record<string, unknown> = {
'system.enableAlphaFunctionality': settings.enableAlphaFunctionality, 'system.enableAlphaFunctionality': settings.enableAlphaFunctionality,
'system.maxDPI': settings.maxDPI, 'system.maxDPI': settings.maxDPI,
'system.enableUrlToPDF': settings.enableUrlToPDF, 'system.enableUrlToPDF': settings.enableUrlToPDF,
@@ -281,9 +282,8 @@ export default function AdminAdvancedSection() {
setManualDownloadLinks([]); setManualDownloadLinks([]);
} catch (error) { } catch (error) {
console.error('[AdminAdvancedSection] Download tessdata languages failed', error); console.error('[AdminAdvancedSection] Download tessdata languages failed', error);
const response = (error as any)?.response; const status = isAxiosError(error) ? error.response?.status : undefined;
const status = response?.status; const serverMessage = isAxiosError(error) ? error.response?.data?.message : undefined;
const serverMessage = response?.data?.message;
if (status === 403) { if (status === 403) {
console.warn('[AdminAdvancedSection] Tessdata directory not writable, falling back to manual download:', serverMessage); console.warn('[AdminAdvancedSection] Tessdata directory not writable, falling back to manual download:', serverMessage);
@@ -309,12 +309,12 @@ export default function AdminAdvancedSection() {
} }
let message: string; let message: string;
if (!response) { if (!isAxiosError(error) || !error.response) {
message = t( message = t(
'admin.settings.advanced.tessdataDir.downloadErrorNetwork', 'admin.settings.advanced.tessdataDir.downloadErrorNetwork',
'Download failed due to a network error. Please check your connection and try again.' 'Download failed due to a network error. Please check your connection and try again.'
); );
} else if (status >= 500) { } else if (status !== undefined && status >= 500) {
message = t( message = t(
'admin.settings.advanced.tessdataDir.downloadErrorServer', 'admin.settings.advanced.tessdataDir.downloadErrorServer',
'The server encountered an error while downloading tessdata languages. Please try again later.' 'The server encountered an error while downloading tessdata languages. Please try again later.'
@@ -1,4 +1,5 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { isAxiosError } from 'axios';
import { Tabs, Loader, Alert, Stack, Text, Button, Accordion } from '@mantine/core'; import { Tabs, Loader, Alert, Stack, Text, Button, Accordion } from '@mantine/core';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
@@ -35,9 +36,9 @@ const AdminAuditSection: React.FC = () => {
setError(null); setError(null);
const status = await auditService.getSystemStatus(); const status = await auditService.getSystemStatus();
setSystemStatus(status); setSystemStatus(status);
} catch (err: any) { } catch (err: unknown) {
// Check if this is a permission/license error (403/404) // Check if this is a permission/license error (403/404)
const status = err?.response?.status; const status = isAxiosError(err) ? err.response?.status : undefined;
if (status === 403 || status === 404) { if (status === 403 || status === 404) {
setError('enterprise-license-required'); setError('enterprise-license-required');
} else { } else {
@@ -44,6 +44,66 @@ interface TelegramSettingsData {
feedback?: FeedbackSettings; feedback?: FeedbackSettings;
} }
interface MailSettings {
enabled?: boolean;
enableInvites?: boolean;
host?: string;
port?: number;
username?: string;
password?: string;
from?: string;
}
interface GoogleDriveSettings {
enabled?: boolean;
clientId?: string;
apiKey?: string;
appId?: string;
}
interface OAuth2GenericSettings {
enabled?: boolean;
provider?: string;
issuer?: string;
clientId?: string;
clientSecret?: string;
scopes?: string;
useAsUsername?: string;
autoCreateUser?: boolean;
blockRegistration?: boolean;
}
interface Saml2Settings {
enabled?: boolean;
provider?: string;
registrationId?: string;
idpMetadataUri?: string;
idpSingleLoginUrl?: string;
idpSingleLogoutUrl?: string;
idpIssuer?: string;
idpCert?: string;
privateKey?: string;
spCert?: string;
autoCreateUser?: boolean;
blockRegistration?: boolean;
}
interface OAuth2ClientSettings {
clientId?: string;
clientSecret?: string;
scopes?: string;
useAsUsername?: string;
issuer?: string;
}
type ProviderSettings =
| MailSettings
| TelegramSettingsData
| GoogleDriveSettings
| OAuth2GenericSettings
| Saml2Settings
| OAuth2ClientSettings;
interface ConnectionsSettingsData { interface ConnectionsSettingsData {
oauth2?: { oauth2?: {
enabled?: boolean; enabled?: boolean;
@@ -55,22 +115,10 @@ interface ConnectionsSettingsData {
blockRegistration?: boolean; blockRegistration?: boolean;
useAsUsername?: string; useAsUsername?: string;
scopes?: string; scopes?: string;
client?: { client?: Record<string, OAuth2ClientSettings>;
[key: string]: any;
};
};
saml2?: {
[key: string]: any;
};
mail?: {
enabled?: boolean;
enableInvites?: boolean;
host?: string;
port?: number;
username?: string;
password?: string;
from?: string;
}; };
saml2?: Saml2Settings;
mail?: MailSettings;
telegram?: TelegramSettingsData; telegram?: TelegramSettingsData;
ssoAutoLogin?: boolean; ssoAutoLogin?: boolean;
enableMobileScanner?: boolean; enableMobileScanner?: boolean;
@@ -93,7 +141,7 @@ export default function AdminConnectionsSection() {
const adminSettings = useAdminSettings<ConnectionsSettingsData>({ const adminSettings = useAdminSettings<ConnectionsSettingsData>({
sectionName: 'connections', sectionName: 'connections',
fetchTransformer: async (): Promise<ConnectionsSettingsData & { _pending?: Record<string, any> }> => { fetchTransformer: async (): Promise<ConnectionsSettingsData & { _pending?: Record<string, unknown> }> => {
// Fetch security settings (oauth2, saml2) // Fetch security settings (oauth2, saml2)
const securityResponse = await apiClient.get('/api/v1/admin/settings/section/security'); const securityResponse = await apiClient.get('/api/v1/admin/settings/section/security');
const securityData = securityResponse.data || {}; const securityData = securityResponse.data || {};
@@ -114,7 +162,7 @@ export default function AdminConnectionsSection() {
const systemResponse = await apiClient.get('/api/v1/admin/settings/section/system'); const systemResponse = await apiClient.get('/api/v1/admin/settings/section/system');
const systemData = systemResponse.data || {}; const systemData = systemResponse.data || {};
const result: ConnectionsSettingsData & { _pending?: Record<string, any> } = { const result: ConnectionsSettingsData & { _pending?: Record<string, unknown> } = {
oauth2: securityData.oauth2 || {}, oauth2: securityData.oauth2 || {},
saml2: securityData.saml2 || {}, saml2: securityData.saml2 || {},
mail: mailData || {}, mail: mailData || {},
@@ -132,7 +180,7 @@ export default function AdminConnectionsSection() {
}; };
// Merge pending blocks from all endpoints // Merge pending blocks from all endpoints
const pendingBlock: Record<string, any> = {}; const pendingBlock: Record<string, unknown> = {};
if (securityData._pending?.oauth2) { if (securityData._pending?.oauth2) {
pendingBlock.oauth2 = securityData._pending.oauth2; pendingBlock.oauth2 = securityData._pending.oauth2;
} }
@@ -183,13 +231,13 @@ export default function AdminConnectionsSection() {
return result; return result;
}, },
saveTransformer: (currentSettings: ConnectionsSettingsData) => { saveTransformer: (currentSettings: ConnectionsSettingsData) => {
const deltaSettings: Record<string, any> = {}; const deltaSettings: Record<string, unknown> = {};
// Build delta for oauth2 settings // Build delta for oauth2 settings
if (currentSettings.oauth2) { if (currentSettings.oauth2) {
Object.keys(currentSettings.oauth2).forEach((key) => { Object.keys(currentSettings.oauth2).forEach((key) => {
if (key !== 'client') { if (key !== 'client') {
deltaSettings[`security.oauth2.${key}`] = (currentSettings.oauth2 as Record<string, any>)[key]; deltaSettings[`security.oauth2.${key}`] = (currentSettings.oauth2 as Record<string, unknown>)[key];
} }
}); });
@@ -197,7 +245,7 @@ export default function AdminConnectionsSection() {
const oauth2Client = currentSettings.oauth2.client; const oauth2Client = currentSettings.oauth2.client;
if (oauth2Client) { if (oauth2Client) {
Object.keys(oauth2Client).forEach((providerId) => { Object.keys(oauth2Client).forEach((providerId) => {
const providerSettings = oauth2Client[providerId]; const providerSettings = oauth2Client[providerId] as Record<string, unknown>;
Object.keys(providerSettings).forEach((key) => { Object.keys(providerSettings).forEach((key) => {
deltaSettings[`security.oauth2.client.${providerId}.${key}`] = providerSettings[key]; deltaSettings[`security.oauth2.client.${providerId}.${key}`] = providerSettings[key];
}); });
@@ -207,22 +255,25 @@ export default function AdminConnectionsSection() {
// Build delta for saml2 settings // Build delta for saml2 settings
if (currentSettings.saml2) { if (currentSettings.saml2) {
Object.keys(currentSettings.saml2).forEach((key) => { const saml2 = currentSettings.saml2 as Record<string, unknown>;
deltaSettings[`security.saml2.${key}`] = (currentSettings.saml2 as Record<string, any>)[key]; Object.keys(saml2).forEach((key) => {
deltaSettings[`security.saml2.${key}`] = saml2[key];
}); });
} }
// Mail settings // Mail settings
if (currentSettings.mail) { if (currentSettings.mail) {
Object.keys(currentSettings.mail).forEach((key) => { const mail = currentSettings.mail as Record<string, unknown>;
deltaSettings[`mail.${key}`] = (currentSettings.mail as Record<string, any>)[key]; Object.keys(mail).forEach((key) => {
deltaSettings[`mail.${key}`] = mail[key];
}); });
} }
// Telegram settings // Telegram settings
if (currentSettings.telegram) { if (currentSettings.telegram) {
Object.keys(currentSettings.telegram).forEach((key) => { const telegram = currentSettings.telegram as Record<string, unknown>;
deltaSettings[`telegram.${key}`] = (currentSettings.telegram as Record<string, any>)[key]; Object.keys(telegram).forEach((key) => {
deltaSettings[`telegram.${key}`] = telegram[key];
}); });
} }
@@ -332,7 +383,7 @@ export default function AdminConnectionsSection() {
return !!(providerSettings?.clientId); return !!(providerSettings?.clientId);
}; };
const getProviderSettings = (provider: Provider): Record<string, any> => { const getProviderSettings = (provider: Provider): ProviderSettings => {
if (provider.id === 'saml2') { if (provider.id === 'saml2') {
return settings?.saml2 || {}; return settings?.saml2 || {};
} }
@@ -346,17 +397,17 @@ export default function AdminConnectionsSection() {
} }
if (provider.id === 'googledrive') { if (provider.id === 'googledrive') {
return { const gd: GoogleDriveSettings = {
enabled: settings?.googleDriveEnabled, enabled: settings?.googleDriveEnabled,
clientId: settings?.googleDriveClientId, clientId: settings?.googleDriveClientId,
apiKey: settings?.googleDriveApiKey, apiKey: settings?.googleDriveApiKey,
appId: settings?.googleDriveAppId, appId: settings?.googleDriveAppId,
}; };
return gd;
} }
if (provider.id === 'oauth2-generic') { if (provider.id === 'oauth2-generic') {
// Generic OAuth2 settings are at the root oauth2 level const generic: OAuth2GenericSettings = {
return {
enabled: settings?.oauth2?.enabled, enabled: settings?.oauth2?.enabled,
provider: settings?.oauth2?.provider, provider: settings?.oauth2?.provider,
issuer: settings?.oauth2?.issuer, issuer: settings?.oauth2?.issuer,
@@ -367,6 +418,7 @@ export default function AdminConnectionsSection() {
autoCreateUser: settings?.oauth2?.autoCreateUser, autoCreateUser: settings?.oauth2?.autoCreateUser,
blockRegistration: settings?.oauth2?.blockRegistration, blockRegistration: settings?.oauth2?.blockRegistration,
}; };
return generic;
} }
// Specific OAuth2 provider settings // Specific OAuth2 provider settings
@@ -386,32 +438,35 @@ export default function AdminConnectionsSection() {
const linkedProviders = allProviders.filter((p) => isProviderConfigured(p)); const linkedProviders = allProviders.filter((p) => isProviderConfigured(p));
const availableProviders = allProviders.filter((p) => !isProviderConfigured(p)); const availableProviders = allProviders.filter((p) => !isProviderConfigured(p));
const updateProviderSettings = (provider: Provider, updatedSettings: Record<string, any>) => { const updateProviderSettings = (provider: Provider, updatedSettings: Record<string, unknown>) => {
if (provider.id === 'smtp') { if (provider.id === 'smtp') {
setSettings({ ...settings, mail: updatedSettings }); setSettings({ ...settings, mail: updatedSettings as MailSettings });
} else if (provider.id === 'telegram') { } else if (provider.id === 'telegram') {
setSettings({ ...settings, telegram: updatedSettings }); setSettings({ ...settings, telegram: updatedSettings as TelegramSettingsData });
} else if (provider.id === 'googledrive') { } else if (provider.id === 'googledrive') {
const gd = updatedSettings as GoogleDriveSettings;
setSettings({ setSettings({
...settings, ...settings,
googleDriveEnabled: updatedSettings.enabled, googleDriveEnabled: gd.enabled,
googleDriveClientId: updatedSettings.clientId, googleDriveClientId: gd.clientId,
googleDriveApiKey: updatedSettings.apiKey, googleDriveApiKey: gd.apiKey,
googleDriveAppId: updatedSettings.appId, googleDriveAppId: gd.appId,
}); });
} else if (provider.id === 'saml2') { } else if (provider.id === 'saml2') {
setSettings({ ...settings, saml2: updatedSettings }); setSettings({ ...settings, saml2: updatedSettings as Saml2Settings });
} else if (provider.id === 'oauth2-generic') { } else if (provider.id === 'oauth2-generic') {
setSettings({ ...settings, oauth2: updatedSettings }); const generic = updatedSettings as OAuth2GenericSettings;
setSettings({ ...settings, oauth2: { ...settings.oauth2, ...generic } });
} else { } else {
// Specific OAuth2 provider // Specific OAuth2 provider
const clientSettings = updatedSettings as OAuth2ClientSettings;
setSettings({ setSettings({
...settings, ...settings,
oauth2: { oauth2: {
...settings.oauth2, ...settings.oauth2,
client: { client: {
...settings.oauth2?.client, ...settings.oauth2?.client,
[provider.id]: updatedSettings [provider.id]: clientSettings,
} }
} }
}); });
@@ -1,4 +1,5 @@
import { useCallback, useEffect, useMemo, useState } from "react"; import { useCallback, useEffect, useMemo, useState } from "react";
import { isAxiosError } from "axios";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { import {
NumberInput, NumberInput,
@@ -68,7 +69,7 @@ export default function AdminDatabaseSection() {
const { settings, setSettings, loading, saving, fetchSettings, saveSettings, isFieldPending } = const { settings, setSettings, loading, saving, fetchSettings, saveSettings, isFieldPending } =
useAdminSettings<DatabaseSettingsData>({ useAdminSettings<DatabaseSettingsData>({
sectionName: "database", sectionName: "database",
fetchTransformer: async (): Promise<DatabaseSettingsData & { _pending?: Record<string, any> }> => { fetchTransformer: async (): Promise<DatabaseSettingsData & { _pending?: Record<string, unknown> }> => {
const response = await apiClient.get("/api/v1/admin/settings/section/system"); const response = await apiClient.get("/api/v1/admin/settings/section/system");
const systemData = response.data || {}; const systemData = response.data || {};
@@ -85,7 +86,7 @@ export default function AdminDatabaseSection() {
}; };
// Map pending changes from system._pending.datasource to root level // Map pending changes from system._pending.datasource to root level
const result: DatabaseSettingsData & { _pending?: Record<string, any> } = { ...datasource }; const result: DatabaseSettingsData & { _pending?: Record<string, unknown> } = { ...datasource };
if (systemData._pending?.datasource) { if (systemData._pending?.datasource) {
result._pending = systemData._pending.datasource; result._pending = systemData._pending.datasource;
} }
@@ -94,7 +95,7 @@ export default function AdminDatabaseSection() {
}, },
saveTransformer: (settings: DatabaseSettingsData) => { saveTransformer: (settings: DatabaseSettingsData) => {
// Convert flat settings to dot-notation for delta endpoint // Convert flat settings to dot-notation for delta endpoint
const deltaSettings: Record<string, any> = { const deltaSettings: Record<string, unknown> = {
"system.datasource.enableCustomDatabase": settings.enableCustomDatabase, "system.datasource.enableCustomDatabase": settings.enableCustomDatabase,
"system.datasource.customDatabaseUrl": settings.customDatabaseUrl, "system.datasource.customDatabaseUrl": settings.customDatabaseUrl,
"system.datasource.username": settings.username, "system.datasource.username": settings.username,
@@ -141,8 +142,8 @@ export default function AdminDatabaseSection() {
const data = await databaseManagementService.getDatabaseData(); const data = await databaseManagementService.getDatabaseData();
setBackupFiles(data.backupFiles || []); setBackupFiles(data.backupFiles || []);
setDatabaseVersion(data.databaseVersion || null); setDatabaseVersion(data.databaseVersion || null);
} catch (error: any) { } catch (error: unknown) {
const message = error?.response?.data?.message || error?.message; const message = isAxiosError(error) ? (error.response?.data?.message || error.message) : undefined;
alert({ alert({
alertType: "error", alertType: "error",
title: t("admin.settings.database.loadError", "Failed to load database backups"), title: t("admin.settings.database.loadError", "Failed to load database backups"),
@@ -189,8 +190,8 @@ export default function AdminDatabaseSection() {
await databaseManagementService.createBackup(); await databaseManagementService.createBackup();
alert({ alertType: "success", title: t("admin.settings.database.backupCreated", "Backup created successfully") }); alert({ alertType: "success", title: t("admin.settings.database.backupCreated", "Backup created successfully") });
await loadBackupData(); await loadBackupData();
} catch (error: any) { } catch (error: unknown) {
const message = error?.response?.data?.message || error?.message; const message = isAxiosError(error) ? (error.response?.data?.message || error.message) : undefined;
alert({ alert({
alertType: "error", alertType: "error",
title: t("admin.settings.database.backupFailed", "Failed to create backup"), title: t("admin.settings.database.backupFailed", "Failed to create backup"),
@@ -209,8 +210,8 @@ export default function AdminDatabaseSection() {
alert({ alertType: "success", title: t("admin.settings.database.importSuccess", "Backup imported successfully") }); alert({ alertType: "success", title: t("admin.settings.database.importSuccess", "Backup imported successfully") });
setUploadFile(null); setUploadFile(null);
await loadBackupData(); await loadBackupData();
} catch (error: any) { } catch (error: unknown) {
const message = error?.response?.data?.message || error?.message; const message = isAxiosError(error) ? (error.response?.data?.message || error.message) : undefined;
alert({ alert({
alertType: "error", alertType: "error",
title: t("admin.settings.database.importFailed", "Failed to import backup"), title: t("admin.settings.database.importFailed", "Failed to import backup"),
@@ -270,8 +271,8 @@ export default function AdminDatabaseSection() {
await databaseManagementService.importFromFileName(fileName); await databaseManagementService.importFromFileName(fileName);
alert({ alertType: "success", title: t("admin.settings.database.importSuccess", "Backup imported successfully") }); alert({ alertType: "success", title: t("admin.settings.database.importSuccess", "Backup imported successfully") });
await loadBackupData(); await loadBackupData();
} catch (error: any) { } catch (error: unknown) {
const message = error?.response?.data?.message || error?.message; const message = isAxiosError(error) ? (error.response?.data?.message || error.message) : undefined;
alert({ alert({
alertType: "error", alertType: "error",
title: t("admin.settings.database.importFailed", "Failed to import backup"), title: t("admin.settings.database.importFailed", "Failed to import backup"),
@@ -289,8 +290,8 @@ export default function AdminDatabaseSection() {
await databaseManagementService.deleteBackup(fileName); await databaseManagementService.deleteBackup(fileName);
alert({ alertType: "success", title: t("admin.settings.database.deleteSuccess", "Backup deleted") }); alert({ alertType: "success", title: t("admin.settings.database.deleteSuccess", "Backup deleted") });
await loadBackupData(); await loadBackupData();
} catch (error: any) { } catch (error: unknown) {
const message = error?.response?.data?.message || error?.message; const message = isAxiosError(error) ? (error.response?.data?.message || error.message) : undefined;
alert({ alert({
alertType: "error", alertType: "error",
title: t("admin.settings.database.deleteFailed", "Failed to delete backup"), title: t("admin.settings.database.deleteFailed", "Failed to delete backup"),
@@ -320,8 +321,8 @@ export default function AdminDatabaseSection() {
link.download = fileName; link.download = fileName;
document.body.appendChild(link); document.body.appendChild(link);
link.click(); link.click();
} catch (error: any) { } catch (error: unknown) {
const message = error?.response?.data?.message || error?.message; const message = isAxiosError(error) ? (error.response?.data?.message || error.message) : undefined;
alert({ alert({
alertType: "error", alertType: "error",
title: t("admin.settings.database.downloadFailed", "Failed to download backup"), title: t("admin.settings.database.downloadFailed", "Failed to download backup"),
@@ -38,11 +38,11 @@ export default function AdminFeaturesSection() {
isFieldPending, isFieldPending,
} = useAdminSettings<FeaturesSettingsData>({ } = useAdminSettings<FeaturesSettingsData>({
sectionName: 'features', sectionName: 'features',
fetchTransformer: async (): Promise<FeaturesSettingsData & { _pending?: Record<string, any> }> => { fetchTransformer: async (): Promise<FeaturesSettingsData & { _pending?: Record<string, unknown> }> => {
const systemResponse = await apiClient.get('/api/v1/admin/settings/section/system'); const systemResponse = await apiClient.get('/api/v1/admin/settings/section/system');
const systemData = systemResponse.data || {}; const systemData = systemResponse.data || {};
const result: FeaturesSettingsData & { _pending?: Record<string, any> } = { const result: FeaturesSettingsData & { _pending?: Record<string, unknown> } = {
serverCertificate: systemData.serverCertificate || { serverCertificate: systemData.serverCertificate || {
enabled: true, enabled: true,
organizationName: 'Stirling-PDF', organizationName: 'Stirling-PDF',
@@ -59,7 +59,7 @@ export default function AdminFeaturesSection() {
return result; return result;
}, },
saveTransformer: (settings: FeaturesSettingsData) => { saveTransformer: (settings: FeaturesSettingsData) => {
const deltaSettings: Record<string, any> = {}; const deltaSettings: Record<string, unknown> = {};
if (settings.serverCertificate) { if (settings.serverCertificate) {
deltaSettings['system.serverCertificate.enabled'] = settings.serverCertificate.enabled; deltaSettings['system.serverCertificate.enabled'] = settings.serverCertificate.enabled;
@@ -91,7 +91,7 @@ export default function AdminGeneralSection() {
isFieldPending, isFieldPending,
} = useAdminSettings<GeneralSettingsData>({ } = useAdminSettings<GeneralSettingsData>({
sectionName: 'general', sectionName: 'general',
fetchTransformer: async (): Promise<GeneralSettingsData & { _pending?: Record<string, any> }> => { fetchTransformer: async (): Promise<GeneralSettingsData & { _pending?: Record<string, unknown> }> => {
const [uiResponse, systemResponse, premiumResponse] = await Promise.all([ const [uiResponse, systemResponse, premiumResponse] = await Promise.all([
apiClient.get('/api/v1/admin/settings/section/ui'), apiClient.get('/api/v1/admin/settings/section/ui'),
apiClient.get('/api/v1/admin/settings/section/system'), apiClient.get('/api/v1/admin/settings/section/system'),
@@ -113,7 +113,7 @@ export default function AdminGeneralSection() {
? watchedFoldersDirs ? watchedFoldersDirs
: (pipelinePaths.watchedFoldersDir ? [pipelinePaths.watchedFoldersDir] : []); : (pipelinePaths.watchedFoldersDir ? [pipelinePaths.watchedFoldersDir] : []);
const result: GeneralSettingsData & { _pending?: Record<string, any> } = { const result: GeneralSettingsData & { _pending?: Record<string, unknown> } = {
ui, ui,
system, system,
customPaths: { customPaths: {
@@ -140,7 +140,7 @@ export default function AdminGeneralSection() {
}; };
// Merge pending blocks from all three endpoints // Merge pending blocks from all three endpoints
const pendingBlock: Record<string, any> = {}; const pendingBlock: Record<string, unknown> = {};
if (ui._pending) { if (ui._pending) {
pendingBlock.ui = ui._pending; pendingBlock.ui = ui._pending;
} }
@@ -161,7 +161,7 @@ export default function AdminGeneralSection() {
return result; return result;
}, },
saveTransformer: (settings: GeneralSettingsData) => { saveTransformer: (settings: GeneralSettingsData) => {
const deltaSettings: Record<string, any> = { const deltaSettings: Record<string, unknown> = {
// UI settings // UI settings
'ui.appNameNavbar': settings.ui?.appNameNavbar, 'ui.appNameNavbar': settings.ui?.appNameNavbar,
'ui.languages': settings.ui?.languages, 'ui.languages': settings.ui?.languages,
@@ -72,12 +72,12 @@ const AdminPlanSection: React.FC = () => {
// Open billing portal in new tab // Open billing portal in new tab
window.open(response.url, '_blank'); window.open(response.url, '_blank');
} catch (error: any) { } catch (error: unknown) {
console.error('Failed to open billing portal:', error); console.error('Failed to open billing portal:', error);
alert({ alert({
alertType: 'error', alertType: 'error',
title: t('billing.portal.error', 'Failed to open billing portal'), title: t('billing.portal.error', 'Failed to open billing portal'),
body: error.message || 'Please try again or contact support.', body: (error instanceof Error ? error.message : undefined) || 'Please try again or contact support.',
}); });
} }
}, [licenseInfo, t, validateLoginEnabled]); }, [licenseInfo, t, validateLoginEnabled]);
@@ -33,7 +33,7 @@ export default function AdminPrivacySection() {
isFieldPending, isFieldPending,
} = useAdminSettings<PrivacySettingsData>({ } = useAdminSettings<PrivacySettingsData>({
sectionName: 'privacy', sectionName: 'privacy',
fetchTransformer: async (): Promise<PrivacySettingsData & { _pending?: Record<string, any> }> => { fetchTransformer: async (): Promise<PrivacySettingsData & { _pending?: Record<string, unknown> }> => {
const [metricsResponse, systemResponse] = await Promise.all([ const [metricsResponse, systemResponse] = await Promise.all([
apiClient.get('/api/v1/admin/settings/section/metrics'), apiClient.get('/api/v1/admin/settings/section/metrics'),
apiClient.get('/api/v1/admin/settings/section/system') apiClient.get('/api/v1/admin/settings/section/system')
@@ -42,14 +42,14 @@ export default function AdminPrivacySection() {
const metrics = metricsResponse.data; const metrics = metricsResponse.data;
const system = systemResponse.data; const system = systemResponse.data;
const result: PrivacySettingsData & { _pending?: Record<string, any> } = { const result: PrivacySettingsData & { _pending?: Record<string, unknown> } = {
enableAnalytics: system.enableAnalytics || false, enableAnalytics: system.enableAnalytics || false,
googleVisibility: system.googlevisibility || false, googleVisibility: system.googlevisibility || false,
metricsEnabled: metrics.enabled || false metricsEnabled: metrics.enabled || false
}; };
// Merge pending blocks from both endpoints // Merge pending blocks from both endpoints
const pendingBlock: Record<string, any> = {}; const pendingBlock: Record<string, unknown> = {};
if (system._pending?.enableAnalytics !== undefined) { if (system._pending?.enableAnalytics !== undefined) {
pendingBlock.enableAnalytics = system._pending.enableAnalytics; pendingBlock.enableAnalytics = system._pending.enableAnalytics;
} }
@@ -67,7 +67,7 @@ export default function AdminSecuritySection() {
isFieldPending, isFieldPending,
} = useAdminSettings<SecuritySettingsData>({ } = useAdminSettings<SecuritySettingsData>({
sectionName: 'security', sectionName: 'security',
fetchTransformer: async (): Promise<SecuritySettingsData & { _pending?: Record<string, any> }> => { fetchTransformer: async (): Promise<SecuritySettingsData & { _pending?: Record<string, unknown> }> => {
const [securityResponse, premiumResponse, systemResponse] = await Promise.all([ const [securityResponse, premiumResponse, systemResponse] = await Promise.all([
apiClient.get('/api/v1/admin/settings/section/security'), apiClient.get('/api/v1/admin/settings/section/security'),
apiClient.get('/api/v1/admin/settings/section/premium'), apiClient.get('/api/v1/admin/settings/section/premium'),
@@ -93,7 +93,7 @@ export default function AdminSecuritySection() {
systemPending: JSON.parse(JSON.stringify(systemPending || {})) systemPending: JSON.parse(JSON.stringify(systemPending || {}))
}); });
const combined: SecuritySettingsData & { _pending?: Record<string, any> } = { const combined: SecuritySettingsData & { _pending?: Record<string, unknown> } = {
...securityActive ...securityActive
}; };
@@ -108,7 +108,7 @@ export default function AdminSecuritySection() {
} }
// Merge all _pending blocks // Merge all _pending blocks
const mergedPending: Record<string, any> = {}; const mergedPending: Record<string, unknown> = {};
if (securityPending) { if (securityPending) {
Object.assign(mergedPending, securityPending); Object.assign(mergedPending, securityPending);
} }
@@ -128,7 +128,7 @@ export default function AdminSecuritySection() {
saveTransformer: (settings: SecuritySettingsData) => { saveTransformer: (settings: SecuritySettingsData) => {
const { audit, html, ...securitySettings } = settings; const { audit, html, ...securitySettings } = settings;
const deltaSettings: Record<string, any> = { const deltaSettings: Record<string, unknown> = {
// Security settings // Security settings
'security.enableLogin': securitySettings.enableLogin, 'security.enableLogin': securitySettings.enableLogin,
'security.loginMethod': securitySettings.loginMethod, 'security.loginMethod': securitySettings.loginMethod,
@@ -155,10 +155,10 @@ const AdminUsageSection: React.FC = () => {
const displayedVisits = endpoints.reduce((sum, e) => sum + e.visits, 0); const displayedVisits = endpoints.reduce((sum, e) => sum + e.visits, 0);
const totalVisits = Number.isFinite(data?.totalVisits) const totalVisits = Number.isFinite(data?.totalVisits)
? Math.max(0, data?.totalVisits as number) ? Math.max(0, data?.totalVisits ?? 0)
: displayedVisits; : displayedVisits;
const totalEndpoints = Number.isFinite(data?.totalEndpoints) const totalEndpoints = Number.isFinite(data?.totalEndpoints)
? Math.max(0, data?.totalEndpoints as number) ? Math.max(0, data?.totalEndpoints ?? 0)
: endpoints.length; : endpoints.length;
const displayedPercentage = totalVisits > 0 const displayedPercentage = totalVisits > 0
@@ -1,4 +1,5 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { isAxiosError } from 'axios';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { import {
Stack, Stack,
@@ -17,6 +18,7 @@ import {
CloseButton, CloseButton,
Avatar, Avatar,
Box, Box,
type ComboboxItem,
} from '@mantine/core'; } from '@mantine/core';
import LocalIcon from '@app/components/shared/LocalIcon'; import LocalIcon from '@app/components/shared/LocalIcon';
import { alert } from '@app/components/toast'; import { alert } from '@app/components/toast';
@@ -114,7 +116,7 @@ export default function PeopleSection() {
...user, ...user,
isActive: adminData.userSessions[user.username] || false, isActive: adminData.userSessions[user.username] || false,
lastRequest: adminData.userLastRequest[user.username] || undefined, lastRequest: adminData.userLastRequest[user.username] || undefined,
mfaEnabled: adminData.userSettings?.[user.username]?.mfaEnabled === 'true', mfaEnabled: (adminData.userSettings?.[user.username] as Record<string, unknown> | undefined)?.mfaEnabled === 'true',
})); }));
setUsers(enrichedUsers); setUsers(enrichedUsers);
@@ -225,12 +227,11 @@ export default function PeopleSection() {
alert({ alertType: 'success', title: t('workspace.people.editMember.success') }); alert({ alertType: 'success', title: t('workspace.people.editMember.success') });
closeEditModal(); closeEditModal();
fetchData(); fetchData();
} catch (error: any) { } catch (error: unknown) {
console.error('[PeopleSection] Failed to update user:', error); console.error('[PeopleSection] Failed to update user:', error);
const errorMessage = error.response?.data?.message || const errorMessage = isAxiosError(error)
error.response?.data?.error || ? (error.response?.data?.message || error.response?.data?.error || error.message)
error.message || : (error instanceof Error ? error.message : undefined) || t('workspace.people.editMember.error');
t('workspace.people.editMember.error');
alert({ alertType: 'error', title: errorMessage }); alert({ alertType: 'error', title: errorMessage });
} finally { } finally {
setProcessing(false); setProcessing(false);
@@ -242,12 +243,11 @@ export default function PeopleSection() {
await userManagementService.toggleUserEnabled(user.username, !user.enabled); await userManagementService.toggleUserEnabled(user.username, !user.enabled);
alert({ alertType: 'success', title: t('workspace.people.toggleEnabled.success') }); alert({ alertType: 'success', title: t('workspace.people.toggleEnabled.success') });
fetchData(); fetchData();
} catch (error: any) { } catch (error: unknown) {
console.error('[PeopleSection] Failed to toggle user status:', error); console.error('[PeopleSection] Failed to toggle user status:', error);
const errorMessage = error.response?.data?.message || const errorMessage = isAxiosError(error)
error.response?.data?.error || ? (error.response?.data?.message || error.response?.data?.error || error.message)
error.message || : (error instanceof Error ? error.message : undefined) || t('workspace.people.toggleEnabled.error');
t('workspace.people.toggleEnabled.error');
alert({ alertType: 'error', title: errorMessage }); alert({ alertType: 'error', title: errorMessage });
} }
}; };
@@ -262,12 +262,12 @@ export default function PeopleSection() {
await userManagementService.deleteUser(user.username); await userManagementService.deleteUser(user.username);
alert({ alertType: 'success', title: t('workspace.people.deleteUserSuccess', 'User deleted successfully') }); alert({ alertType: 'success', title: t('workspace.people.deleteUserSuccess', 'User deleted successfully') });
fetchData(); fetchData();
} catch (error: any) { } catch (error: unknown) {
console.error('[PeopleSection] Failed to delete user:', error); console.error('[PeopleSection] Failed to delete user:', error);
const errorMessage = error.response?.data?.message || const errorMessage = isAxiosError(error)
error.response?.data?.error || ? (error.response?.data?.message || error.response?.data?.error || error.message)
error.message || : (error instanceof Error ? error.message : undefined) ||
t('workspace.people.deleteUserError', 'Failed to delete user'); t('workspace.people.deleteUserError', 'Failed to delete user');
alert({ alertType: 'error', title: errorMessage }); alert({ alertType: 'error', title: errorMessage });
} }
}; };
@@ -282,12 +282,11 @@ export default function PeopleSection() {
await userManagementService.unlockUser(user.username); await userManagementService.unlockUser(user.username);
alert({ alertType: 'success', title: t('workspace.people.unlockUserSuccess', 'User account unlocked successfully') }); alert({ alertType: 'success', title: t('workspace.people.unlockUserSuccess', 'User account unlocked successfully') });
fetchData(); fetchData();
} catch (error: any) { } catch (error: unknown) {
console.error('[PeopleSection] Failed to unlock user:', error); console.error('[PeopleSection] Failed to unlock user:', error);
const errorMessage = error.response?.data?.message || const errorMessage = isAxiosError(error)
error.response?.data?.error || ? (error.response?.data?.message || error.response?.data?.error || error.message)
error.message || : (error instanceof Error ? error.message : undefined) || t('workspace.people.unlockUserError', 'Failed to unlock user account');
t('workspace.people.unlockUserError', 'Failed to unlock user account');
alert({ alertType: 'error', title: errorMessage }); alert({ alertType: 'error', title: errorMessage });
} }
}; };
@@ -339,9 +338,9 @@ export default function PeopleSection() {
}, },
]; ];
const renderRoleOption = ({ option }: { option: any }) => ( const renderRoleOption = ({ option }: { option: ComboboxItem & { icon?: string; description?: string } }) => (
<Group gap="sm" wrap="nowrap"> <Group gap="sm" wrap="nowrap">
<LocalIcon icon={option.icon} width="1.25rem" height="1.25rem" style={{ flexShrink: 0 }} /> <LocalIcon icon={option.icon ?? ''} width="1.25rem" height="1.25rem" style={{ flexShrink: 0 }} />
<Box style={{ flex: 1 }}> <Box style={{ flex: 1 }}>
<Text size="sm" fw={500}>{option.label}</Text> <Text size="sm" fw={500}>{option.label}</Text>
<Text size="xs" c="dimmed" style={{ whiteSpace: 'normal', lineHeight: 1.4 }}> <Text size="xs" c="dimmed" style={{ whiteSpace: 'normal', lineHeight: 1.4 }}>
@@ -668,12 +667,12 @@ export default function PeopleSection() {
try { try {
await userManagementService.disableMfaByAdmin(user.username); await userManagementService.disableMfaByAdmin(user.username);
alert({ alertType: 'success', title: t('workspace.people.mfa.adminDisableSuccess', 'MFA disabled successfully for user') }); alert({ alertType: 'success', title: t('workspace.people.mfa.adminDisableSuccess', 'MFA disabled successfully for user') });
} catch (error: any) { } catch (error: unknown) {
console.error('[PeopleSection] Failed to disable MFA for user:', error); console.error('[PeopleSection] Failed to disable MFA for user:', error);
const errorMessage = error.response?.data?.message || const errorMessage = isAxiosError(error)
error.response?.data?.error || ? (error.response?.data?.message || error.response?.data?.error || error.message)
error.message || : (error instanceof Error ? error.message : undefined) ||
t('workspace.people.mfa.adminDisableError', 'Failed to disable MFA for user'); t('workspace.people.mfa.adminDisableError', 'Failed to disable MFA for user');
alert({ alertType: 'error', title: errorMessage }); alert({ alertType: 'error', title: errorMessage });
} }
}} }}
@@ -1,4 +1,5 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { isAxiosError } from 'axios';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { import {
Stack, Stack,
@@ -110,12 +111,11 @@ export default function TeamDetailsSection({ teamId, onBack }: TeamDetailsSectio
setAddMemberModalOpened(false); setAddMemberModalOpened(false);
setSelectedUserId(''); setSelectedUserId('');
fetchTeamDetails(); fetchTeamDetails();
} catch (error: any) { } catch (error: unknown) {
console.error('Failed to add member:', error); console.error('Failed to add member:', error);
const errorMessage = error.response?.data?.message || const errorMessage = isAxiosError(error)
error.response?.data?.error || ? (error.response?.data?.message || error.response?.data?.error || error.message)
error.message || : (error instanceof Error ? error.message : undefined) || t('workspace.teams.addMemberToTeam.error', 'Failed to add user to team');
t('workspace.teams.addMemberToTeam.error', 'Failed to add user to team');
alert({ alertType: 'error', title: errorMessage }); alert({ alertType: 'error', title: errorMessage });
} finally { } finally {
setProcessing(false); setProcessing(false);
@@ -140,12 +140,11 @@ export default function TeamDetailsSection({ teamId, onBack }: TeamDetailsSectio
await teamService.moveUserToTeam(user.username, user.rolesAsString || 'ROLE_USER', defaultTeam.id); await teamService.moveUserToTeam(user.username, user.rolesAsString || 'ROLE_USER', defaultTeam.id);
alert({ alertType: 'success', title: t('workspace.teams.removeMemberSuccess', 'User removed from team') }); alert({ alertType: 'success', title: t('workspace.teams.removeMemberSuccess', 'User removed from team') });
fetchTeamDetails(); fetchTeamDetails();
} catch (error: any) { } catch (error: unknown) {
console.error('Failed to remove member:', error); console.error('Failed to remove member:', error);
const errorMessage = error.response?.data?.message || const errorMessage = isAxiosError(error)
error.response?.data?.error || ? (error.response?.data?.message || error.response?.data?.error || error.message)
error.message || : (error instanceof Error ? error.message : undefined) || t('workspace.teams.removeMemberError', 'Failed to remove user from team');
t('workspace.teams.removeMemberError', 'Failed to remove user from team');
alert({ alertType: 'error', title: errorMessage }); alert({ alertType: 'error', title: errorMessage });
} finally { } finally {
setProcessing(false); setProcessing(false);
@@ -163,12 +162,12 @@ export default function TeamDetailsSection({ teamId, onBack }: TeamDetailsSectio
await userManagementService.deleteUser(user.username); await userManagementService.deleteUser(user.username);
alert({ alertType: 'success', title: t('workspace.people.deleteUserSuccess', 'User deleted successfully') }); alert({ alertType: 'success', title: t('workspace.people.deleteUserSuccess', 'User deleted successfully') });
fetchTeamDetails(); fetchTeamDetails();
} catch (error: any) { } catch (error: unknown) {
console.error('Failed to delete user:', error); console.error('Failed to delete user:', error);
const errorMessage = error.response?.data?.message || const errorMessage = isAxiosError(error)
error.response?.data?.error || ? (error.response?.data?.message || error.response?.data?.error || error.message)
error.message || : (error instanceof Error ? error.message : undefined) ||
t('workspace.people.deleteUserError', 'Failed to delete user'); t('workspace.people.deleteUserError', 'Failed to delete user');
alert({ alertType: 'error', title: errorMessage }); alert({ alertType: 'error', title: errorMessage });
} finally { } finally {
setProcessing(false); setProcessing(false);
@@ -185,12 +184,11 @@ export default function TeamDetailsSection({ teamId, onBack }: TeamDetailsSectio
await userManagementService.unlockUser(user.username); await userManagementService.unlockUser(user.username);
alert({ alertType: 'success', title: t('workspace.people.unlockUserSuccess', 'User account unlocked successfully') }); alert({ alertType: 'success', title: t('workspace.people.unlockUserSuccess', 'User account unlocked successfully') });
fetchTeamDetails(); fetchTeamDetails();
} catch (error: any) { } catch (error: unknown) {
console.error('[TeamDetailsSection] Failed to unlock user:', error); console.error('[TeamDetailsSection] Failed to unlock user:', error);
const errorMessage = error.response?.data?.message || const errorMessage = isAxiosError(error)
error.response?.data?.error || ? (error.response?.data?.message || error.response?.data?.error || error.message)
error.message || : (error instanceof Error ? error.message : undefined) || t('workspace.people.unlockUserError', 'Failed to unlock user account');
t('workspace.people.unlockUserError', 'Failed to unlock user account');
alert({ alertType: 'error', title: errorMessage }); alert({ alertType: 'error', title: errorMessage });
} }
}; };
@@ -225,12 +223,12 @@ export default function TeamDetailsSection({ teamId, onBack }: TeamDetailsSectio
setSelectedUser(null); setSelectedUser(null);
setSelectedTeamId(''); setSelectedTeamId('');
fetchTeamDetails(); fetchTeamDetails();
} catch (error: any) { } catch (error: unknown) {
console.error('Failed to change team:', error); console.error('Failed to change team:', error);
const errorMessage = error.response?.data?.message || const errorMessage = isAxiosError(error)
error.response?.data?.error || ? (error.response?.data?.message || error.response?.data?.error || error.message)
error.message || : (error instanceof Error ? error.message : undefined) ||
t('workspace.teams.changeTeam.error', 'Failed to change team'); t('workspace.teams.changeTeam.error', 'Failed to change team');
alert({ alertType: 'error', title: errorMessage }); alert({ alertType: 'error', title: errorMessage });
} finally { } finally {
setProcessing(false); setProcessing(false);
@@ -1,4 +1,5 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { isAxiosError } from 'axios';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { import {
Stack, Stack,
@@ -83,12 +84,11 @@ export default function TeamsSection() {
setNewTeamName(''); setNewTeamName('');
setCreateModalOpened(false); setCreateModalOpened(false);
await fetchTeams(); await fetchTeams();
} catch (error: any) { } catch (error: unknown) {
console.error('Failed to create team:', error); console.error('Failed to create team:', error);
const errorMessage = error.response?.data?.message || const errorMessage = isAxiosError(error)
error.response?.data?.error || ? (error.response?.data?.message || error.response?.data?.error || error.message)
error.message || : (error instanceof Error ? error.message : undefined) || t('workspace.teams.createTeam.error');
t('workspace.teams.createTeam.error');
alert({ alertType: 'error', title: errorMessage }); alert({ alertType: 'error', title: errorMessage });
} finally { } finally {
setProcessing(false); setProcessing(false);
@@ -109,12 +109,11 @@ export default function TeamsSection() {
setSelectedTeam(null); setSelectedTeam(null);
setRenameModalOpened(false); setRenameModalOpened(false);
await fetchTeams(); await fetchTeams();
} catch (error: any) { } catch (error: unknown) {
console.error('Failed to rename team:', error); console.error('Failed to rename team:', error);
const errorMessage = error.response?.data?.message || const errorMessage = isAxiosError(error)
error.response?.data?.error || ? (error.response?.data?.message || error.response?.data?.error || error.message)
error.message || : (error instanceof Error ? error.message : undefined) || t('workspace.teams.renameTeam.error');
t('workspace.teams.renameTeam.error');
alert({ alertType: 'error', title: errorMessage }); alert({ alertType: 'error', title: errorMessage });
} finally { } finally {
setProcessing(false); setProcessing(false);
@@ -135,12 +134,12 @@ export default function TeamsSection() {
await teamService.deleteTeam(team.id); await teamService.deleteTeam(team.id);
alert({ alertType: 'success', title: t('workspace.teams.deleteTeam.success') }); alert({ alertType: 'success', title: t('workspace.teams.deleteTeam.success') });
await fetchTeams(); await fetchTeams();
} catch (error: any) { } catch (error: unknown) {
console.error('Failed to delete team:', error); console.error('Failed to delete team:', error);
const errorMessage = error.response?.data?.message || const errorMessage = isAxiosError(error)
error.response?.data?.error || ? (error.response?.data?.message || error.response?.data?.error || error.message)
error.message || : (error instanceof Error ? error.message : undefined) ||
t('workspace.teams.deleteTeam.error'); t('workspace.teams.deleteTeam.error');
alert({ alertType: 'error', title: errorMessage }); alert({ alertType: 'error', title: errorMessage });
} }
}; };
@@ -15,7 +15,7 @@ import {
UnstyledButton, UnstyledButton,
} from '@mantine/core'; } from '@mantine/core';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import auditService, { AuditEvent } from '@app/services/auditService'; import auditService, { AuditEvent, AuditFilters } from '@app/services/auditService';
import { Z_INDEX_OVER_CONFIG_MODAL } from '@app/styles/zIndex'; import { Z_INDEX_OVER_CONFIG_MODAL } from '@app/styles/zIndex';
import { useAuditFilters } from '@app/hooks/useAuditFilters'; import { useAuditFilters } from '@app/hooks/useAuditFilters';
import AuditFiltersForm from '@app/components/shared/config/configSections/audit/AuditFiltersForm'; import AuditFiltersForm from '@app/components/shared/config/configSections/audit/AuditFiltersForm';
@@ -122,7 +122,7 @@ const AuditEventsTable: React.FC<AuditEventsTableProps> = ({
}, [filters, currentPage, loginEnabled]); }, [filters, currentPage, loginEnabled]);
// Wrap filter handlers to reset pagination // Wrap filter handlers to reset pagination
const handleFilterChangeWithReset = (key: keyof typeof filters, value: any) => { const handleFilterChangeWithReset = (key: keyof AuditFilters, value: AuditFilters[keyof AuditFilters]) => {
handleFilterChange(key, value); handleFilterChange(key, value);
setCurrentPage(1); setCurrentPage(1);
}; };
@@ -170,8 +170,8 @@ const AuditEventsTable: React.FC<AuditEventsTableProps> = ({
// Apply sorting to current events // Apply sorting to current events
const sortedEvents = [...events].sort((a, b) => { const sortedEvents = [...events].sort((a, b) => {
let aVal: any; let aVal: string | number | undefined;
let bVal: any; let bVal: string | number | undefined;
switch (sortKey) { switch (sortKey) {
case 'timestamp': case 'timestamp':
@@ -296,14 +296,14 @@ const AuditEventsTable: React.FC<AuditEventsTableProps> = ({
let author = ''; let author = '';
let fileHash = ''; let fileHash = '';
if (event.details && typeof event.details === 'object') { if (event.details && typeof event.details === 'object') {
const details = event.details as Record<string, any>; const details = event.details as Record<string, unknown>;
const files = details.files; const files = details.files;
if (Array.isArray(files) && files.length > 0) { if (Array.isArray(files) && files.length > 0) {
const firstFile = files[0] as Record<string, any>; const firstFile = files[0] as Record<string, unknown>;
documentName = firstFile.name || ''; documentName = typeof firstFile.name === 'string' ? firstFile.name : '';
if (showAuthor || showFileHash) { if (showAuthor || showFileHash) {
author = firstFile.pdfAuthor || ''; author = typeof firstFile.pdfAuthor === 'string' ? firstFile.pdfAuthor : '';
fileHash = firstFile.fileHash ? firstFile.fileHash.substring(0, 16) + '...' : ''; fileHash = typeof firstFile.fileHash === 'string' ? firstFile.fileHash.substring(0, 16) + '...' : '';
} }
} }
} }
@@ -51,7 +51,7 @@ const AuditExportSection: React.FC<AuditExportSectionProps> = ({
try { try {
setExporting(true); setExporting(true);
const fieldsParam = Object.keys(selectedFields).filter(k => selectedFields[k as keyof typeof selectedFields]).join(','); const fieldsParam = Object.keys(selectedFields).filter(k => selectedFields[k]).join(',');
const blob = await auditService.exportData(exportFormat, { ...filters, fields: fieldsParam }); const blob = await auditService.exportData(exportFormat, { ...filters, fields: fieldsParam });
@@ -43,7 +43,7 @@ interface AuditFiltersFormProps {
filters: AuditFilters; filters: AuditFilters;
eventTypes: string[]; eventTypes: string[];
users: string[]; users: string[];
onFilterChange: (key: keyof AuditFilters, value: any) => void; onFilterChange: (key: keyof AuditFilters, value: AuditFilters[keyof AuditFilters]) => void;
onClearFilters: () => void; onClearFilters: () => void;
disabled?: boolean; disabled?: boolean;
} }
@@ -147,8 +147,8 @@ const AuditFiltersForm: React.FC<AuditFiltersFormProps> = ({
<DateInput <DateInput
placeholder={t('audit.events.startDate', 'Start date')} placeholder={t('audit.events.startDate', 'Start date')}
value={filters.startDate ? new Date(filters.startDate) : null} value={filters.startDate ? new Date(filters.startDate) : null}
onChange={(value: any) => { onChange={(value) => {
onFilterChange('startDate', value ? formatDateToYMD(value as Date) : undefined); onFilterChange('startDate', value ? formatDateToYMD(new Date(value)) : undefined);
}} }}
clearable clearable
disabled={disabled} disabled={disabled}
@@ -157,8 +157,8 @@ const AuditFiltersForm: React.FC<AuditFiltersFormProps> = ({
<DateInput <DateInput
placeholder={t('audit.events.endDate', 'End date')} placeholder={t('audit.events.endDate', 'End date')}
value={filters.endDate ? new Date(filters.endDate) : null} value={filters.endDate ? new Date(filters.endDate) : null}
onChange={(value: any) => { onChange={(value) => {
onFilterChange('endDate', value ? formatDateToYMD(value as Date) : undefined); onFilterChange('endDate', value ? formatDateToYMD(new Date(value)) : undefined);
}} }}
clearable clearable
disabled={disabled} disabled={disabled}
@@ -14,7 +14,7 @@ export const useLicensePolling = (
const pollForLicenseKey = useCallback(async (installId: string) => { const pollForLicenseKey = useCallback(async (installId: string) => {
// Use shared polling utility // Use shared polling utility
const result = await pollLicenseKeyWithBackoff(installId, { const result = await pollLicenseKeyWithBackoff(installId, {
isMounted: () => isMountedRef.current!, isMounted: () => isMountedRef.current ?? false,
onStatusChange: setPollingStatus, onStatusChange: setPollingStatus,
}); });
@@ -23,7 +23,7 @@ export const useLicensePolling = (
// Activate the license key // Activate the license key
const activation = await activateLicenseKey(result.licenseKey, { const activation = await activateLicenseKey(result.licenseKey, {
isMounted: () => isMountedRef.current!, isMounted: () => isMountedRef.current ?? false,
onActivated: onLicenseActivated, onActivated: onLicenseActivated,
}); });
@@ -112,8 +112,8 @@ const ParticipantView: React.FC<ParticipantViewProps> = ({ token }) => {
showLogo: true, showLogo: true,
}); });
setNotification({ type: 'success', message: 'Signature submitted successfully!' }); setNotification({ type: 'success', message: 'Signature submitted successfully!' });
} catch (err: any) { } catch (err: unknown) {
setNotification({ type: 'error', message: `Failed to submit signature: ${err.message}` }); setNotification({ type: 'error', message: `Failed to submit signature: ${err instanceof Error ? err.message : String(err)}` });
} finally { } finally {
setIsSubmitting(false); setIsSubmitting(false);
} }
@@ -125,8 +125,8 @@ const ParticipantView: React.FC<ParticipantViewProps> = ({ token }) => {
try { try {
await decline(token, declineReason || 'Declined by participant'); await decline(token, declineReason || 'Declined by participant');
setNotification({ type: 'success', message: 'You have declined this signing request.' }); setNotification({ type: 'success', message: 'You have declined this signing request.' });
} catch (err: any) { } catch (err: unknown) {
setNotification({ type: 'error', message: `Failed to decline: ${err.message}` }); setNotification({ type: 'error', message: `Failed to decline: ${err instanceof Error ? err.message : String(err)}` });
} }
} }
}; };
@@ -8,6 +8,7 @@ import {
type ReactNode, type ReactNode,
} from 'react'; } from 'react';
import apiClient from '@app/services/apiClient'; import apiClient from '@app/services/apiClient';
import { isAxiosError } from 'axios';
import { useAppConfig } from '@app/contexts/AppConfigContext'; import { useAppConfig } from '@app/contexts/AppConfigContext';
import { useAuth } from '@app/auth/UseSession'; import { useAuth } from '@app/auth/UseSession';
import { useLicense } from '@app/contexts/LicenseContext'; import { useLicense } from '@app/contexts/LicenseContext';
@@ -95,13 +96,8 @@ function getErrorMessage(error: unknown): string {
if (typeof error === 'string') { if (typeof error === 'string') {
return error; return error;
} }
if ( if (isAxiosError(error) && typeof error.response?.data?.message === 'string') {
typeof error === 'object' && return error.response.data.message;
error !== null &&
'response' in error &&
typeof (error as any).response?.data?.message === 'string'
) {
return (error as any).response.data.message;
} }
if (error instanceof Error) { if (error instanceof Error) {
return error.message; return error.message;
@@ -196,7 +192,7 @@ export function ServerExperienceProvider({ children }: { children: ReactNode })
( (
await apiClient.get<{ totalUsers?: number }>( await apiClient.get<{ totalUsers?: number }>(
'/api/v1/proprietary/ui-data/admin-settings', '/api/v1/proprietary/ui-data/admin-settings',
{ suppressErrorToast: true } as any, { suppressErrorToast: true },
) )
).data; ).data;
const totalUsers = const totalUsers =
@@ -219,7 +215,7 @@ export function ServerExperienceProvider({ children }: { children: ReactNode })
( (
await apiClient.get<WeeklyActiveUsersResponse>('/api/v1/info/wau', { await apiClient.get<WeeklyActiveUsersResponse>('/api/v1/info/wau', {
suppressErrorToast: true, suppressErrorToast: true,
} as any) })
).data; ).data;
const weeklyActiveUsers = const weeklyActiveUsers =
typeof responseData?.weeklyActiveUsers === 'number' typeof responseData?.weeklyActiveUsers === 'number'
@@ -1,4 +1,5 @@
import { useState, useCallback, useEffect } from 'react'; import { useState, useCallback, useEffect } from 'react';
import { isAxiosError } from 'axios';
import workflowService, { import workflowService, {
WorkflowSessionResponse, WorkflowSessionResponse,
ParticipantResponse, ParticipantResponse,
@@ -35,9 +36,10 @@ export const useParticipantSession = (token?: string): UseParticipantSessionResu
]); ]);
setSession(sessionData); setSession(sessionData);
setParticipant(participantData); setParticipant(participantData);
} catch (err: any) { } catch (err: unknown) {
const errorMsg = const errorMsg = isAxiosError(err)
err.response?.data?.message || err.message || 'Failed to load session'; ? (err.response?.data?.message || err.message)
: (err instanceof Error ? err.message : undefined) || 'Failed to load session';
setError(errorMsg); setError(errorMsg);
} finally { } finally {
setLoading(false); setLoading(false);
@@ -55,9 +57,10 @@ export const useParticipantSession = (token?: string): UseParticipantSessionResu
if (request.participantToken) { if (request.participantToken) {
await loadSession(request.participantToken); await loadSession(request.participantToken);
} }
} catch (err: any) { } catch (err: unknown) {
const errorMsg = const errorMsg = isAxiosError(err)
err.response?.data?.message || err.message || 'Failed to submit signature'; ? (err.response?.data?.message || err.message)
: (err instanceof Error ? err.message : undefined) || 'Failed to submit signature';
setError(errorMsg); setError(errorMsg);
throw new Error(errorMsg, { cause: err }); throw new Error(errorMsg, { cause: err });
} finally { } finally {
@@ -79,9 +82,10 @@ export const useParticipantSession = (token?: string): UseParticipantSessionResu
setParticipant(updatedParticipant); setParticipant(updatedParticipant);
// Reload session // Reload session
await loadSession(token); await loadSession(token);
} catch (err: any) { } catch (err: unknown) {
const errorMsg = const errorMsg = isAxiosError(err)
err.response?.data?.message || err.message || 'Failed to decline'; ? (err.response?.data?.message || err.message)
: (err instanceof Error ? err.message : undefined) || 'Failed to decline';
setError(errorMsg); setError(errorMsg);
throw new Error(errorMsg, { cause: err }); throw new Error(errorMsg, { cause: err });
} finally { } finally {
@@ -104,9 +108,10 @@ export const useParticipantSession = (token?: string): UseParticipantSessionResu
a.click(); a.click();
window.URL.revokeObjectURL(url); window.URL.revokeObjectURL(url);
document.body.removeChild(a); document.body.removeChild(a);
} catch (err: any) { } catch (err: unknown) {
const errorMsg = const errorMsg = isAxiosError(err)
err.response?.data?.message || err.message || 'Failed to download document'; ? (err.response?.data?.message || err.message)
: (err instanceof Error ? err.message : undefined) || 'Failed to download document';
setError(errorMsg); setError(errorMsg);
} finally { } finally {
setLoading(false); setLoading(false);
@@ -1,4 +1,5 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { isAxiosError } from 'axios';
import { useParams, useNavigate } from 'react-router-dom'; import { useParams, useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Stack, Text, Paper, Center, Loader, TextInput, PasswordInput, Anchor } from '@mantine/core'; import { Stack, Text, Paper, Center, Loader, TextInput, PasswordInput, Anchor } from '@mantine/core';
@@ -56,14 +57,14 @@ export default function InviteAccept() {
setLoading(true); setLoading(true);
const response = await apiClient.get<InviteData>(`/api/v1/invite/validate/${token}`, { const response = await apiClient.get<InviteData>(`/api/v1/invite/validate/${token}`, {
suppressErrorToast: true, suppressErrorToast: true,
} as any); });
setInviteData(response.data); setInviteData(response.data);
setError(null); setError(null);
} catch (err: any) { } catch (err: unknown) {
const errorMessage = const errorMessage = isAxiosError(err)
err.response?.data?.error || ? (err.response?.data?.error || err.message)
err.message || : (err instanceof Error ? err.message : undefined) ||
t('invite.validationError', 'Failed to validate invitation link'); t('invite.validationError', 'Failed to validate invitation link');
setError(errorMessage); setError(errorMessage);
} finally { } finally {
setLoading(false); setLoading(false);
@@ -108,15 +109,15 @@ export default function InviteAccept() {
await apiClient.post(`/api/v1/invite/accept/${token}`, formData, { await apiClient.post(`/api/v1/invite/accept/${token}`, formData, {
suppressErrorToast: true, suppressErrorToast: true,
} as any); });
// Success - redirect to login // Success - redirect to login
navigate('/login?messageType=accountCreated'); navigate('/login?messageType=accountCreated');
} catch (err: any) { } catch (err: unknown) {
const errorMessage = const errorMessage = isAxiosError(err)
err.response?.data?.error || ? (err.response?.data?.error || err.message)
err.message || : (err instanceof Error ? err.message : undefined) ||
t('invite.acceptError', 'Failed to create account'); t('invite.acceptError', 'Failed to create account');
setError(errorMessage); setError(errorMessage);
} finally { } finally {
setSubmitting(false); setSubmitting(false);
@@ -1,4 +1,5 @@
import { useEffect, useMemo, useRef } from 'react'; import { useEffect, useMemo, useRef } from 'react';
import { isAxiosError } from 'axios';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import apiClient from '@app/services/apiClient'; import apiClient from '@app/services/apiClient';
@@ -99,7 +100,7 @@ export default function ShareLinkLoader({ token }: ShareLinkLoaderProps) {
const idMap = new Map<string, FileId>(); const idMap = new Map<string, FileId>();
for (let i = 0; i < stirlingFiles.length; i += 1) { for (let i = 0; i < stirlingFiles.length; i += 1) {
idMap.set(sortedEntries[i].logicalId, stirlingFiles[i].fileId as FileId); idMap.set(sortedEntries[i].logicalId, stirlingFiles[i].fileId);
} }
const rootIdMap = new Map<string, FileId>(); const rootIdMap = new Map<string, FileId>();
@@ -199,9 +200,9 @@ export default function ShareLinkLoader({ token }: ShareLinkLoaderProps) {
navActions.setWorkbench('viewer'); navActions.setWorkbench('viewer');
navigate('/', { replace: true }); navigate('/', { replace: true });
} catch (error: any) { } catch (error: unknown) {
if (signal.aborted) return; if (signal.aborted) return;
const status = error?.response?.status; const status = isAxiosError(error) ? error.response?.status : undefined;
if (status === 401 || status === 403) { if (status === 401 || status === 403) {
if (!isAuthenticated && !authLoading) { if (!isAuthenticated && !authLoading) {
alert({ alert({
@@ -1,4 +1,5 @@
import { useCallback, useEffect, useMemo, useState } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react';
import { isAxiosError } from 'axios';
import { useNavigate, useParams } from 'react-router-dom'; import { useNavigate, useParams } from 'react-router-dom';
import { Alert, Badge, Button, Group, Loader, Paper, Stack, Text, Title } from '@mantine/core'; import { Alert, Badge, Button, Group, Loader, Paper, Stack, Text, Title } from '@mantine/core';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@@ -45,8 +46,8 @@ export default function ShareLinkPage() {
const data = await fetchShareLinkMetadata(normalizedToken); const data = await fetchShareLinkMetadata(normalizedToken);
setMetadata(data); setMetadata(data);
setStatus('ready'); setStatus('ready');
} catch (error: any) { } catch (error: unknown) {
const statusCode = error?.response?.status as number | undefined; const statusCode = isAxiosError(error) ? error.response?.status : undefined;
if (statusCode === 401) { if (statusCode === 401) {
setStatus('login'); setStatus('login');
} else if (statusCode === 403) { } else if (statusCode === 403) {
@@ -83,8 +84,8 @@ export default function ShareLinkPage() {
link.click(); link.click();
link.remove(); link.remove();
URL.revokeObjectURL(url); URL.revokeObjectURL(url);
} catch (error: any) { } catch (error: unknown) {
const statusCode = error?.response?.status as number | undefined; const statusCode = isAxiosError(error) ? error.response?.status : undefined;
if (statusCode === 401) { if (statusCode === 401) {
setStatus('login'); setStatus('login');
} else if (statusCode === 403) { } else if (statusCode === 403) {
@@ -118,8 +119,8 @@ export default function ShareLinkPage() {
} }
navActions.setWorkbench('viewer'); navActions.setWorkbench('viewer');
navigate('/', { replace: true }); navigate('/', { replace: true });
} catch (error: any) { } catch (error: unknown) {
const statusCode = error?.response?.status as number | undefined; const statusCode = isAxiosError(error) ? error.response?.status : undefined;
if (statusCode === 401) { if (statusCode === 401) {
setStatus('login'); setStatus('login');
} else if (statusCode === 403) { } else if (statusCode === 403) {
@@ -24,7 +24,7 @@ export interface ShareLinkMetadata {
export async function fetchShareLinkMetadata(token: string): Promise<ShareLinkMetadata> { export async function fetchShareLinkMetadata(token: string): Promise<ShareLinkMetadata> {
const response = await apiClient.get<ShareLinkMetadata>( const response = await apiClient.get<ShareLinkMetadata>(
`/api/v1/storage/share-links/${token}/metadata`, `/api/v1/storage/share-links/${token}/metadata`,
{ suppressErrorToast: true, skipAuthRedirect: true } as any { suppressErrorToast: true, skipAuthRedirect: true }
); );
return response.data || {}; return response.data || {};
} }
@@ -38,7 +38,7 @@ export async function downloadShareLink(token: string): Promise<{
responseType: 'blob', responseType: 'blob',
suppressErrorToast: true, suppressErrorToast: true,
skipAuthRedirect: true, skipAuthRedirect: true,
} as any); });
const contentType = const contentType =
(response.headers && (response.headers['content-type'] || response.headers['Content-Type'])) || (response.headers && (response.headers['content-type'] || response.headers['Content-Type'])) ||
''; '';
@@ -73,7 +73,7 @@ export async function importShareLinkToWorkbench(
const idMap = new Map<string, FileId>(); const idMap = new Map<string, FileId>();
for (let i = 0; i < stirlingFiles.length; i += 1) { for (let i = 0; i < stirlingFiles.length; i += 1) {
idMap.set(sortedEntries[i].logicalId, stirlingFiles[i].fileId as FileId); idMap.set(sortedEntries[i].logicalId, stirlingFiles[i].fileId);
} }
const rootIdMap = new Map<string, FileId>(); const rootIdMap = new Map<string, FileId>();
@@ -141,7 +141,7 @@ export async function importShareLinkToWorkbench(
autoUnzip: false, autoUnzip: false,
skipAutoUnzip: false, skipAutoUnzip: false,
}); });
const ids = stirlingFiles.map((stirlingFile: StirlingFile) => stirlingFile.fileId as FileId); const ids = stirlingFiles.map((stirlingFile: StirlingFile) => stirlingFile.fileId);
if (ids.length > 0) { if (ids.length > 0) {
const sharedUpdates = { const sharedUpdates = {
remoteStorageId: shareMetadata?.fileId, remoteStorageId: shareMetadata?.fileId,
@@ -1,4 +1,5 @@
import apiClient from '@app/services/apiClient'; import apiClient from '@app/services/apiClient';
import type { User } from '@app/services/userManagementService';
export interface Team { export interface Team {
id: number; id: number;
@@ -25,6 +26,13 @@ export interface TeamDetailsResponse {
availableUsers: TeamMember[]; availableUsers: TeamMember[];
} }
export interface TeamDetailsUIResponse {
team: Team;
teamUsers: User[];
availableUsers: User[];
userLastRequest?: Record<string, number>;
}
/** /**
* Team Management Service * Team Management Service
* Provides functions to interact with team-related backend APIs * Provides functions to interact with team-related backend APIs
@@ -41,8 +49,8 @@ export const teamService = {
/** /**
* Get team details including members * Get team details including members
*/ */
async getTeamDetails(teamId: number): Promise<any> { async getTeamDetails(teamId: number): Promise<TeamDetailsUIResponse> {
const response = await apiClient.get(`/api/v1/proprietary/ui-data/teams/${teamId}`); const response = await apiClient.get<TeamDetailsUIResponse>(`/api/v1/proprietary/ui-data/teams/${teamId}`);
return response.data; return response.data;
}, },
@@ -54,7 +62,7 @@ export const teamService = {
formData.append('name', name); formData.append('name', name);
await apiClient.post('/api/v1/team/create', formData, { await apiClient.post('/api/v1/team/create', formData, {
suppressErrorToast: true, suppressErrorToast: true,
} as any); });
}, },
/** /**
@@ -66,7 +74,7 @@ export const teamService = {
formData.append('newName', newName); formData.append('newName', newName);
await apiClient.post('/api/v1/team/rename', formData, { await apiClient.post('/api/v1/team/rename', formData, {
suppressErrorToast: true, suppressErrorToast: true,
} as any); });
}, },
/** /**
@@ -77,7 +85,7 @@ export const teamService = {
formData.append('teamId', teamId.toString()); formData.append('teamId', teamId.toString());
await apiClient.post('/api/v1/team/delete', formData, { await apiClient.post('/api/v1/team/delete', formData, {
suppressErrorToast: true, suppressErrorToast: true,
} as any); });
}, },
/** /**
@@ -89,7 +97,7 @@ export const teamService = {
formData.append('userId', userId.toString()); formData.append('userId', userId.toString());
await apiClient.post('/api/v1/team/addUser', formData, { await apiClient.post('/api/v1/team/addUser', formData, {
suppressErrorToast: true, suppressErrorToast: true,
} as any); });
}, },
/** /**
@@ -102,6 +110,6 @@ export const teamService = {
formData.append('teamId', teamId.toString()); formData.append('teamId', teamId.toString());
await apiClient.post('/api/v1/user/admin/changeRole', formData, { await apiClient.post('/api/v1/user/admin/changeRole', formData, {
suppressErrorToast: true, suppressErrorToast: true,
} as any); });
}, },
}; };
@@ -30,7 +30,7 @@ export interface AdminSettingsData {
disabledUsers: number; disabledUsers: number;
currentUsername?: string; currentUsername?: string;
roleDetails?: Record<string, string>; roleDetails?: Record<string, string>;
teams?: any[]; teams?: unknown[];
maxPaidUsers?: number; maxPaidUsers?: number;
// License information // License information
maxAllowedUsers: number; maxAllowedUsers: number;
@@ -39,7 +39,7 @@ export interface AdminSettingsData {
licenseMaxUsers: number; licenseMaxUsers: number;
premiumEnabled: boolean; premiumEnabled: boolean;
mailEnabled: boolean; mailEnabled: boolean;
userSettings?: Record<string, any>; userSettings?: Record<string, unknown>;
lockedUsers?: string[]; lockedUsers?: string[];
} }
@@ -155,7 +155,7 @@ export const userManagementService = {
} }
await apiClient.post('/api/v1/user/admin/saveUser', formData, { await apiClient.post('/api/v1/user/admin/saveUser', formData, {
suppressErrorToast: true, // Component will handle error display suppressErrorToast: true, // Component will handle error display
} as any); });
}, },
/** /**
@@ -170,7 +170,7 @@ export const userManagementService = {
} }
await apiClient.post('/api/v1/user/admin/changeRole', formData, { await apiClient.post('/api/v1/user/admin/changeRole', formData, {
suppressErrorToast: true, suppressErrorToast: true,
} as any); });
}, },
/** /**
@@ -181,7 +181,7 @@ export const userManagementService = {
formData.append('enabled', enabled.toString()); formData.append('enabled', enabled.toString());
await apiClient.post(`/api/v1/user/admin/changeUserEnabled/${username}`, formData, { await apiClient.post(`/api/v1/user/admin/changeUserEnabled/${username}`, formData, {
suppressErrorToast: true, suppressErrorToast: true,
} as any); });
}, },
/** /**
@@ -190,7 +190,7 @@ export const userManagementService = {
async deleteUser(username: string): Promise<void> { async deleteUser(username: string): Promise<void> {
await apiClient.post(`/api/v1/user/admin/deleteUser/${username}`, null, { await apiClient.post(`/api/v1/user/admin/deleteUser/${username}`, null, {
suppressErrorToast: true, suppressErrorToast: true,
} as any); });
}, },
/** /**
@@ -211,7 +211,7 @@ export const userManagementService = {
formData, formData,
{ {
suppressErrorToast: true, // Component will handle error display suppressErrorToast: true, // Component will handle error display
} as any }
); );
return response.data; return response.data;
@@ -245,7 +245,7 @@ export const userManagementService = {
formData, formData,
{ {
suppressErrorToast: true, suppressErrorToast: true,
} as any }
); );
return response.data; return response.data;
@@ -265,7 +265,7 @@ export const userManagementService = {
async revokeInviteLink(inviteId: number): Promise<void> { async revokeInviteLink(inviteId: number): Promise<void> {
await apiClient.delete(`/api/v1/invite/revoke/${inviteId}`, { await apiClient.delete(`/api/v1/invite/revoke/${inviteId}`, {
suppressErrorToast: true, suppressErrorToast: true,
} as any); });
}, },
/** /**
@@ -300,7 +300,7 @@ export const userManagementService = {
await apiClient.post('/api/v1/user/admin/changePasswordForUser', formData, { await apiClient.post('/api/v1/user/admin/changePasswordForUser', formData, {
suppressErrorToast: true, // Component will handle error display suppressErrorToast: true, // Component will handle error display
} as any); });
}, },
/** /**
@@ -309,7 +309,7 @@ export const userManagementService = {
async unlockUser(username: string): Promise<void> { async unlockUser(username: string): Promise<void> {
await apiClient.post(`/api/v1/user/admin/unlockUser/${username}`, null, { await apiClient.post(`/api/v1/user/admin/unlockUser/${username}`, null, {
suppressErrorToast: true, suppressErrorToast: true,
} as any); });
}, },
/** /**