mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-16 19:33:11 +02:00
Fix any type usage in the saas/ folder (#5934)
# Description of Changes Ages ago I made #4835 to try and fix all the `any` type usage in the system but never got it finished, and there were just too many to review and ensure it still worked. There's even more now. My new tactic is to fix folder by folder. This fixes the `any` typing in the `saas/` folder, and also enables `no-unnecessary-type-assertion`, which really helps reduce pointless `as` casts that AI generates when the type is already known. I hope to expand both of these to the rest of the folders soon, but one folder is better than none.
This commit is contained in:
@@ -38,16 +38,16 @@ export function ManageBillingButton({
|
||||
setLoading(true);
|
||||
setErr(null);
|
||||
try {
|
||||
const { data, error } = await supabase.functions.invoke('manage-billing', {
|
||||
const { data, error } = await supabase.functions.invoke<{ url: string; error?: string }>('manage-billing', {
|
||||
body: {
|
||||
name: 'Functions',
|
||||
return_url: returnUrl},
|
||||
})
|
||||
if (error) throw error;
|
||||
if (!data || 'error' in data) throw new Error((data as any)?.error ?? 'No portal URL');
|
||||
window.location.href = (data as any).url;
|
||||
} catch (e: any) {
|
||||
setErr(e.message ?? 'Could not open billing portal');
|
||||
if (!data || 'error' in data) throw new Error(data?.error ?? 'No portal URL');
|
||||
window.location.href = data.url;
|
||||
} catch (e: unknown) {
|
||||
setErr(e instanceof Error ? e.message : 'Could not open billing portal');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ interface StripeCheckoutProps {
|
||||
currency?: string;
|
||||
isTrialConversion?: boolean;
|
||||
// Proprietary-specific props (for compatibility)
|
||||
planGroup?: any;
|
||||
planGroup?: unknown;
|
||||
minimumSeats?: number;
|
||||
onLicenseActivated?: (licenseInfo: {licenseType: string; enabled: boolean; maxUsers: number; hasKey: boolean}) => void;
|
||||
hostedCheckoutSuccess?: {
|
||||
|
||||
@@ -162,7 +162,7 @@ export function createScale(domain: [number, number], range: [number, number]) {
|
||||
* @param wait The wait time in milliseconds
|
||||
* @returns Debounced function
|
||||
*/
|
||||
export function debounce<T extends (...args: any[]) => any>(
|
||||
export function debounce<T extends (...args: unknown[]) => unknown>(
|
||||
func: T,
|
||||
wait: number
|
||||
): (...args: Parameters<T>) => void {
|
||||
|
||||
@@ -149,8 +149,8 @@ const Overview: React.FC<OverviewProps> = ({ onLogoutClick }) => {
|
||||
|
||||
// Clear success message after 3 seconds
|
||||
setTimeout(() => setSuccess(null), 3000);
|
||||
} catch (error: any) {
|
||||
setProfileError(error.message || 'Failed to switch to custom picture');
|
||||
} catch (error: unknown) {
|
||||
setProfileError(error instanceof Error ? error.message : 'Failed to switch to custom picture');
|
||||
} finally {
|
||||
setProfileUploading(false);
|
||||
}
|
||||
@@ -181,8 +181,8 @@ const Overview: React.FC<OverviewProps> = ({ onLogoutClick }) => {
|
||||
setSuccess('Account upgraded successfully! You can now sign in with your email.');
|
||||
setEmail('');
|
||||
setPassword('');
|
||||
} catch (err: any) {
|
||||
setUpgradeError(err?.message || 'Failed to upgrade account');
|
||||
} catch (err: unknown) {
|
||||
setUpgradeError(err instanceof Error ? err.message : 'Failed to upgrade account');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
@@ -404,7 +404,7 @@ const Overview: React.FC<OverviewProps> = ({ onLogoutClick }) => {
|
||||
style={{ width: 16, height: 16 }}
|
||||
/>
|
||||
}
|
||||
onClick={() => handleOAuthUpgrade(provider.id as any)}
|
||||
onClick={() => handleOAuthUpgrade(provider.id as 'github' | 'google' | 'apple' | 'azure')}
|
||||
disabled={isLoading}
|
||||
>
|
||||
{provider.label}
|
||||
|
||||
@@ -55,8 +55,8 @@ const PasswordSecurity: React.FC = () => {
|
||||
setOpened(false);
|
||||
setDidUpdate(false);
|
||||
}, 2000);
|
||||
} catch (e: any) {
|
||||
setError(e?.message || 'Failed to change password');
|
||||
} catch (e: unknown) {
|
||||
setError(e instanceof Error ? e.message : 'Failed to change password');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
|
||||
+16
-16
@@ -1,8 +1,11 @@
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { isAxiosError } from "axios";
|
||||
import apiClient from "@app/services/apiClient";
|
||||
import { useAuth } from "@app/auth/UseSession";
|
||||
import { isUserAnonymous } from "@app/auth/supabase";
|
||||
|
||||
type ApiKeyResponse = string | { apiKey?: string };
|
||||
|
||||
export function useApiKey() {
|
||||
const { session, loading, user } = useAuth();
|
||||
const isAnonymous = Boolean(user && isUserAnonymous(user));
|
||||
@@ -17,24 +20,21 @@ export function useApiKey() {
|
||||
setError(null);
|
||||
try {
|
||||
// Backend is POST for get and update
|
||||
const res = await apiClient.post("/api/v1/user/get-api-key");
|
||||
const value = typeof res.data === "string" ? res.data : res.data?.apiKey;
|
||||
const res = await apiClient.post<ApiKeyResponse>("/api/v1/user/get-api-key");
|
||||
const value = typeof res.data === "string" ? res.data : res.data.apiKey;
|
||||
if (typeof value === "string") setApiKey(value);
|
||||
} catch (e: any) {
|
||||
} catch (e: unknown) {
|
||||
// If not found, try to create one by calling update endpoint
|
||||
if (e?.response?.status === 404) {
|
||||
if (isAxiosError(e) && e.response?.status === 404) {
|
||||
try {
|
||||
const createRes = await apiClient.post("/api/v1/user/update-api-key");
|
||||
const created =
|
||||
typeof createRes.data === "string"
|
||||
? createRes.data
|
||||
: createRes.data?.apiKey;
|
||||
const createRes = await apiClient.post<ApiKeyResponse>("/api/v1/user/update-api-key");
|
||||
const created = typeof createRes.data === "string" ? createRes.data : createRes.data.apiKey;
|
||||
if (typeof created === "string") setApiKey(created);
|
||||
} catch (createErr: any) {
|
||||
setError(createErr);
|
||||
} catch (createErr: unknown) {
|
||||
setError(createErr instanceof Error ? createErr : new Error(String(createErr)));
|
||||
}
|
||||
} else {
|
||||
setError(e);
|
||||
setError(e instanceof Error ? e : new Error(String(e)));
|
||||
}
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
@@ -46,11 +46,11 @@ export function useApiKey() {
|
||||
setIsRefreshing(true);
|
||||
setError(null);
|
||||
try {
|
||||
const res = await apiClient.post("/api/v1/user/update-api-key");
|
||||
const value = typeof res.data === "string" ? res.data : res.data?.apiKey;
|
||||
const res = await apiClient.post<ApiKeyResponse>("/api/v1/user/update-api-key");
|
||||
const value = typeof res.data === "string" ? res.data : res.data.apiKey;
|
||||
if (typeof value === "string") setApiKey(value);
|
||||
} catch (e: any) {
|
||||
setError(e);
|
||||
} catch (e: unknown) {
|
||||
setError(e instanceof Error ? e : new Error(String(e)));
|
||||
} finally {
|
||||
setIsRefreshing(false);
|
||||
}
|
||||
|
||||
+7
-7
@@ -6,10 +6,10 @@ import { isUserAnonymous } from "@app/auth/supabase";
|
||||
|
||||
function coerceNumber(value: unknown, fallback = 0): number {
|
||||
const n = typeof value === "string" ? Number(value) : (value as number);
|
||||
return Number.isFinite(n) ? (n as number) : fallback;
|
||||
return Number.isFinite(n) ? n : fallback;
|
||||
}
|
||||
|
||||
function normalizeCredits(raw: any): ApiCredits {
|
||||
function normalizeCredits(raw: Record<string, unknown>): ApiCredits {
|
||||
// Accept a variety of possible backend keys to be resilient
|
||||
return {
|
||||
weeklyCreditsRemaining: coerceNumber(
|
||||
@@ -28,9 +28,9 @@ function normalizeCredits(raw: any): ApiCredits {
|
||||
raw?.totalAvailableCredits ?? raw?.totalRemaining ?? raw?.available_total
|
||||
),
|
||||
weeklyResetDate:
|
||||
(raw?.weeklyResetDate ?? raw?.weeklyReset ?? raw?.reset_date) || "",
|
||||
String(raw?.weeklyResetDate ?? raw?.weeklyReset ?? raw?.reset_date ?? ""),
|
||||
lastApiUsage:
|
||||
(raw?.lastApiUsage ?? raw?.lastApiUse ?? raw?.last_used_at) || "",
|
||||
String(raw?.lastApiUsage ?? raw?.lastApiUse ?? raw?.last_used_at ?? ""),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ export function useCredits() {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const res = await apiClient.get("/api/v1/credits");
|
||||
const res = await apiClient.get<Record<string, unknown>>("/api/v1/credits");
|
||||
const normalized = normalizeCredits(res.data);
|
||||
// If backend returns an "empty" payload, keep data null so the UI stays in loading/skeleton
|
||||
const isEmpty = !normalized.weeklyCreditsAllocated &&
|
||||
@@ -57,8 +57,8 @@ export function useCredits() {
|
||||
!normalized.weeklyResetDate &&
|
||||
!normalized.lastApiUsage;
|
||||
setData(isEmpty ? null : normalized);
|
||||
} catch (e: any) {
|
||||
setError(e);
|
||||
} catch (e: unknown) {
|
||||
setError(e instanceof Error ? e : new Error(String(e)));
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
setHasAttempted(true);
|
||||
|
||||
+3
-3
@@ -7,9 +7,9 @@ import PlanCard from '@app/components/shared/config/configSections/plan/PlanCard
|
||||
interface AvailablePlansSectionProps {
|
||||
plans: PlanTier[];
|
||||
currentPlan?: PlanTier;
|
||||
currentLicenseInfo?: any;
|
||||
onUpgradeClick: (plan: PlanTier | any) => void; // Accept PlanTierGroup for compatibility
|
||||
onManageClick?: (plan: PlanTier | any) => void; // Accept PlanTierGroup for compatibility
|
||||
currentLicenseInfo?: unknown;
|
||||
onUpgradeClick: (plan: PlanTier) => void;
|
||||
onManageClick?: (plan: PlanTier) => void;
|
||||
currency?: string;
|
||||
onCurrencyChange?: (currency: string) => void;
|
||||
currencyOptions?: Array<{ value: string; label: string }>;
|
||||
|
||||
@@ -5,15 +5,15 @@ import { PlanTier } from '@app/hooks/usePlans';
|
||||
|
||||
interface PlanCardProps {
|
||||
plan?: PlanTier;
|
||||
planGroup?: any; // For proprietary PlanTierGroup compatibility
|
||||
planGroup?: { monthly?: PlanTier; yearly?: PlanTier }; // For proprietary PlanTierGroup compatibility
|
||||
isCurrentPlan?: boolean;
|
||||
isCurrentTier?: boolean;
|
||||
isDowngrade?: boolean;
|
||||
isUserProOrAbove?: boolean;
|
||||
currentLicenseInfo?: any;
|
||||
currentLicenseInfo?: unknown;
|
||||
currentTier?: string | null; // Accept null for proprietary compatibility
|
||||
onUpgradeClick?: (plan: any) => void; // Accept PlanTierGroup or PlanTier
|
||||
onManageClick?: (plan: any) => void;
|
||||
onUpgradeClick?: (plan: PlanTier) => void;
|
||||
onManageClick?: (plan: PlanTier) => void;
|
||||
loginEnabled?: boolean;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ const PlanCard: React.FC<PlanCardProps> = ({
|
||||
loginEnabled: _loginEnabled
|
||||
}) => {
|
||||
// Use plan from props, or extract from planGroup if proprietary is using it
|
||||
const plan = propPlan || (planGroup as any)?.monthly || (planGroup as any)?.yearly;
|
||||
const plan = propPlan || planGroup?.monthly || planGroup?.yearly;
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (!plan) return null; // Safety check
|
||||
|
||||
Reference in New Issue
Block a user