mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-07-28 04:38:59 +00:00
fix(lint): resolve all ESLint errors to pass CI lint step
- Replace catch (error: any) with catch (error) + (error as Error).message cast in EditorLayout, NodeManager, HomeDashboard, AppStoreView - Define TemplateVolume interface in AppStoreView; replace volumes any[] with typed array - Define MetricPoint interface in HomeDashboard; replace metrics any[] with MetricPoint[] - Define TerminalContainer type in BashExecModal; replace as any DOM property casts - Define NodeTestInfo interface in NodeManager; replace info: any with typed shape - Fix DockerNetworkStats cast in EditorLayout container stats WebSocket handler - Remove unused catch variable (e) in api.ts and other components - Cast streamFilter onValueChange val to union type in GlobalObservabilityView - Add eslint-disable-next-line react-refresh/only-export-components to badge.tsx, button.tsx, AuthContext, NodeContext, use-data-state, use-is-in-view - Add eslint-disable-next-line react-hooks/set-state-in-effect in LogViewer - Add /* eslint-disable */ to animate-ui third-party primitive files
This commit is contained in:
@@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- **Added:** `isValidStackName`, `isValidRemoteUrl`, `isPathWithinBase` extracted to `backend/src/utils/validation.ts` for reuse and testability.
|
||||
|
||||
### Fixed
|
||||
- **Fixed:** ESLint CI step now passes with zero errors — replaced all `any` type annotations with proper types or `unknown` casts, fixed unused catch variables, suppressed `react-refresh/only-export-components` on files that intentionally export both a component and a hook/constant (contexts, badge, button), added file-level `eslint-disable` on animate-ui third-party primitives, and added `eslint-disable-next-line react-hooks/set-state-in-effect` for the LogViewer state reset pattern.
|
||||
- **Fixed:** Four empty `catch {}` blocks in `EditorLayout` (mark-all-read, delete notification, clear-all notifications, image update fetch) now surface errors via `toast.error()` instead of silently swallowing them.
|
||||
- **Fixed:** `ErrorBoundary` component existed but was not connected — it now wraps the root `<App />` in `main.tsx`, catching crashes in any context provider or route component.
|
||||
- **Fixed:** `AlertDialogContent` used `asChild` on `AlertDialogPrimitive.Content` with a `motion.div` wrapper — Radix internally injects a second child (`DescriptionWarning`), causing `React.Children.only` to throw and the ErrorBoundary to trigger whenever the delete-stack confirmation dialog opened. Replaced with CSS keyframe animations (`data-[state=open]:animate-in` / `data-[state=closed]:animate-out`) which don't require `asChild`.
|
||||
|
||||
@@ -17,6 +17,11 @@ export interface TemplateEnv {
|
||||
default?: string;
|
||||
}
|
||||
|
||||
interface TemplateVolume {
|
||||
container?: string;
|
||||
bind?: string;
|
||||
}
|
||||
|
||||
export interface Template {
|
||||
type?: number;
|
||||
title: string;
|
||||
@@ -24,7 +29,7 @@ export interface Template {
|
||||
logo?: string;
|
||||
image?: string;
|
||||
ports?: string[];
|
||||
volumes?: any[];
|
||||
volumes?: TemplateVolume[];
|
||||
env?: TemplateEnv[];
|
||||
categories?: string[];
|
||||
github_url?: string;
|
||||
@@ -68,8 +73,8 @@ export function AppStoreView({ onDeploySuccess }: AppStoreViewProps) {
|
||||
if (!res.ok) throw new Error('Failed to fetch templates');
|
||||
const data = await res.json();
|
||||
setTemplates(data || []);
|
||||
} catch (err: any) {
|
||||
toast.error(err.message || "Failed to load App Shop");
|
||||
} catch (err) {
|
||||
toast.error((err as Error).message || "Failed to load App Shop");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -95,7 +100,7 @@ export function AppStoreView({ onDeploySuccess }: AppStoreViewProps) {
|
||||
|
||||
// Initialize Volumes
|
||||
const initVols: Record<string, string> = {};
|
||||
t.volumes?.forEach((v: any) => {
|
||||
t.volumes?.forEach((v) => {
|
||||
if (v.container) {
|
||||
initVols[v.container] = v.bind || `./${v.container.split('/').filter(Boolean).pop() || 'data'}`;
|
||||
}
|
||||
@@ -146,7 +151,7 @@ export function AppStoreView({ onDeploySuccess }: AppStoreViewProps) {
|
||||
|
||||
// Process Volumes
|
||||
if (modifiedTemplate.volumes) {
|
||||
modifiedTemplate.volumes = modifiedTemplate.volumes.map((v: any) => {
|
||||
modifiedTemplate.volumes = modifiedTemplate.volumes.map((v) => {
|
||||
if (v.container && volVars[v.container] !== undefined) {
|
||||
return { ...v, bind: volVars[v.container] };
|
||||
}
|
||||
@@ -175,8 +180,8 @@ export function AppStoreView({ onDeploySuccess }: AppStoreViewProps) {
|
||||
toast.success(`${selectedTemplate?.title} deployed successfully!`);
|
||||
setIsSheetOpen(false);
|
||||
onDeploySuccess(stackName.trim());
|
||||
} catch (err: any) {
|
||||
toast.error(err.message || 'Deployment failed');
|
||||
} catch (err) {
|
||||
toast.error((err as Error).message || 'Deployment failed');
|
||||
} finally {
|
||||
setIsDeploying(false);
|
||||
}
|
||||
@@ -399,14 +404,15 @@ export function AppStoreView({ onDeploySuccess }: AppStoreViewProps) {
|
||||
{selectedTemplate.volumes && selectedTemplate.volumes.length > 0 && (
|
||||
<div className="space-y-4 pt-4 border-t">
|
||||
<h4 className="font-semibold">Volumes (Host : Container)</h4>
|
||||
{selectedTemplate.volumes.map((v: any, idx: number) => {
|
||||
if (!v.container) return null;
|
||||
{selectedTemplate.volumes.map((v, idx: number) => {
|
||||
const containerPath = v.container;
|
||||
if (!containerPath) return null;
|
||||
return (
|
||||
<div key={idx} className="space-y-1.5">
|
||||
<Label className="text-xs text-muted-foreground font-mono">Container: {v.container}</Label>
|
||||
<Label className="text-xs text-muted-foreground font-mono">Container: {containerPath}</Label>
|
||||
<Input
|
||||
value={volVars[v.container] !== undefined ? volVars[v.container] : ''}
|
||||
onChange={(e) => setVolVars(prev => ({ ...prev, [v.container]: e.target.value }))}
|
||||
value={volVars[containerPath] !== undefined ? volVars[containerPath] : ''}
|
||||
onChange={(e) => setVolVars(prev => ({ ...prev, [containerPath]: e.target.value }))}
|
||||
placeholder={`/path/to/host/dir`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -5,6 +5,8 @@ import { Terminal } from '@xterm/xterm';
|
||||
import { FitAddon } from '@xterm/addon-fit';
|
||||
import '@xterm/xterm/css/xterm.css';
|
||||
|
||||
type TerminalContainer = HTMLDivElement & { __resizeObserver?: ResizeObserver };
|
||||
|
||||
interface BashExecModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
@@ -191,14 +193,15 @@ export default function BashExecModal({ isOpen, onClose, containerId, containerN
|
||||
resizeObserver.observe(containerEl);
|
||||
|
||||
// Store observer cleanup on the container element for later
|
||||
(containerEl as any).__resizeObserver = resizeObserver;
|
||||
(containerEl as TerminalContainer).__resizeObserver = resizeObserver;
|
||||
}
|
||||
|
||||
return () => {
|
||||
// Clean up ResizeObserver
|
||||
if (terminalRef.current && (terminalRef.current as any).__resizeObserver) {
|
||||
(terminalRef.current as any).__resizeObserver.disconnect();
|
||||
delete (terminalRef.current as any).__resizeObserver;
|
||||
const el = terminalRef.current as TerminalContainer | null;
|
||||
if (el?.__resizeObserver) {
|
||||
el.__resizeObserver.disconnect();
|
||||
delete el.__resizeObserver;
|
||||
}
|
||||
};
|
||||
}, [isOpen, containerId, cleanup]);
|
||||
|
||||
@@ -492,7 +492,7 @@ export default function EditorLayout() {
|
||||
let currentRx = 0;
|
||||
let currentTx = 0;
|
||||
if (data.networks) {
|
||||
Object.values(data.networks).forEach((net: any) => {
|
||||
Object.values(data.networks as Record<string, { rx_bytes?: number; tx_bytes?: number }>).forEach((net) => {
|
||||
currentRx += net.rx_bytes || 0;
|
||||
currentTx += net.tx_bytes || 0;
|
||||
});
|
||||
@@ -713,9 +713,9 @@ export default function EditorLayout() {
|
||||
const conts = await containersRes.json();
|
||||
setContainers(Array.isArray(conts) ? conts : []);
|
||||
await refreshStacks(true);
|
||||
} catch (error: any) {
|
||||
} catch (error) {
|
||||
console.error('Failed to deploy:', error);
|
||||
toast.error(error.message || 'Failed to deploy stack');
|
||||
toast.error((error as Error).message || 'Failed to deploy stack');
|
||||
} finally {
|
||||
setLoadingAction(null);
|
||||
}
|
||||
@@ -741,9 +741,9 @@ export default function EditorLayout() {
|
||||
const conts = await containersRes.json();
|
||||
setContainers(Array.isArray(conts) ? conts : []);
|
||||
await refreshStacks(true);
|
||||
} catch (error: any) {
|
||||
} catch (error) {
|
||||
console.error('Failed to stop:', error);
|
||||
toast.error(error.message || 'Failed to stop stack');
|
||||
toast.error((error as Error).message || 'Failed to stop stack');
|
||||
} finally {
|
||||
setLoadingAction(null);
|
||||
}
|
||||
@@ -769,9 +769,9 @@ export default function EditorLayout() {
|
||||
const conts = await containersRes.json();
|
||||
setContainers(Array.isArray(conts) ? conts : []);
|
||||
await refreshStacks(true);
|
||||
} catch (error: any) {
|
||||
} catch (error) {
|
||||
console.error('Failed to restart:', error);
|
||||
toast.error(error.message || 'Failed to restart stack');
|
||||
toast.error((error as Error).message || 'Failed to restart stack');
|
||||
} finally {
|
||||
setLoadingAction(null);
|
||||
}
|
||||
@@ -797,9 +797,9 @@ export default function EditorLayout() {
|
||||
const conts = await containersRes.json();
|
||||
setContainers(Array.isArray(conts) ? conts : []);
|
||||
await refreshStacks(true);
|
||||
} catch (error: any) {
|
||||
} catch (error) {
|
||||
console.error('Failed to update:', error);
|
||||
toast.error(error.message || 'Failed to update stack');
|
||||
toast.error((error as Error).message || 'Failed to update stack');
|
||||
} finally {
|
||||
setLoadingAction(null);
|
||||
}
|
||||
@@ -830,9 +830,9 @@ export default function EditorLayout() {
|
||||
setIsEditing(false);
|
||||
}
|
||||
await refreshStacks();
|
||||
} catch (error: any) {
|
||||
} catch (error) {
|
||||
console.error('Failed to delete stack:', error);
|
||||
toast.error(error.message || 'Failed to delete stack');
|
||||
toast.error((error as Error).message || 'Failed to delete stack');
|
||||
} finally {
|
||||
setLoadingAction(null);
|
||||
}
|
||||
@@ -860,9 +860,9 @@ export default function EditorLayout() {
|
||||
await refreshStacks();
|
||||
// Auto-load the new stack in the editor pane
|
||||
await loadFile(stackName);
|
||||
} catch (error: any) {
|
||||
} catch (error) {
|
||||
console.error('Failed to create stack:', error);
|
||||
toast.error(error.message || 'Failed to create stack');
|
||||
toast.error((error as Error).message || 'Failed to create stack');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ export function GlobalObservabilityView() {
|
||||
const entry: LogEntry = JSON.parse(event.data);
|
||||
entry._id = ++logIdRef.current;
|
||||
bufferRef.current.push(entry);
|
||||
} catch (e) { /* ignore parse errors */ }
|
||||
} catch { /* ignore parse errors */ }
|
||||
};
|
||||
|
||||
eventSource.onerror = () => {
|
||||
@@ -243,7 +243,7 @@ export function GlobalObservabilityView() {
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
|
||||
<Select value={streamFilter} onValueChange={(val: any) => setStreamFilter(val)}>
|
||||
<Select value={streamFilter} onValueChange={(val) => setStreamFilter(val as 'ALL' | 'STDOUT' | 'STDERR')}>
|
||||
<SelectTrigger className="w-[110px] h-8 text-sm">
|
||||
<SelectValue placeholder="Stream" />
|
||||
</SelectTrigger>
|
||||
|
||||
@@ -18,6 +18,12 @@ interface Stats {
|
||||
inactive: number;
|
||||
}
|
||||
|
||||
interface MetricPoint {
|
||||
timestamp: number;
|
||||
cpu_percent: number;
|
||||
memory_mb: number;
|
||||
}
|
||||
|
||||
interface SystemStats {
|
||||
cpu: {
|
||||
usage: string;
|
||||
@@ -62,7 +68,7 @@ export default function HomeDashboard() {
|
||||
const [newStackName, setNewStackName] = useState('');
|
||||
const [stats, setStats] = useState<Stats>({ active: 0, exited: 0, total: 0, inactive: 0 });
|
||||
const [systemStats, setSystemStats] = useState<SystemStats | null>(null);
|
||||
const [metrics, setMetrics] = useState<any[]>([]);
|
||||
const [metrics, setMetrics] = useState<MetricPoint[]>([]);
|
||||
|
||||
// Fetch container stats - re-runs when active node changes so stale data is cleared immediately
|
||||
useEffect(() => {
|
||||
@@ -194,9 +200,9 @@ export default function HomeDashboard() {
|
||||
setConvertedYaml('');
|
||||
setDockerRunInput('');
|
||||
window.location.reload(); // Refresh to show new stack
|
||||
} catch (error: any) {
|
||||
} catch (error) {
|
||||
console.error('Failed to create stack:', error);
|
||||
toast.error(error?.message || error?.error || 'Failed to create stack');
|
||||
toast.error((error as Error).message || 'Failed to create stack');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ export function LogViewer({ containerId, containerName, isOpen, onClose }: LogVi
|
||||
useEffect(() => {
|
||||
if (!isOpen || !containerId) return;
|
||||
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect
|
||||
setLogs([]);
|
||||
setIsConnected(false);
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ export function NodeManager() {
|
||||
const [editingNodeId, setEditingNodeId] = useState<number | null>(null);
|
||||
const [deletingNode, setDeletingNode] = useState<Node | null>(null);
|
||||
const [testing, setTesting] = useState<number | null>(null);
|
||||
const [testResult, setTestResult] = useState<{ nodeId: number; info: any } | null>(null);
|
||||
const [testResult, setTestResult] = useState<{ nodeId: number; info: { serverVersion?: string; os?: string; architecture?: string; containers?: number; images?: number; cpus?: number } } | null>(null);
|
||||
|
||||
// Node token generation state
|
||||
const [generatedToken, setGeneratedToken] = useState<string | null>(null);
|
||||
@@ -84,8 +84,8 @@ export function NodeManager() {
|
||||
}
|
||||
|
||||
await refreshNodes();
|
||||
} catch (error: any) {
|
||||
toast.error(error.message || 'Failed to create node');
|
||||
} catch (error) {
|
||||
toast.error((error as Error).message || 'Failed to create node');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -105,8 +105,8 @@ export function NodeManager() {
|
||||
setEditingNodeId(null);
|
||||
setFormData(defaultFormData);
|
||||
await refreshNodes();
|
||||
} catch (error: any) {
|
||||
toast.error(error.message || 'Failed to update node');
|
||||
} catch (error) {
|
||||
toast.error((error as Error).message || 'Failed to update node');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -135,8 +135,8 @@ export function NodeManager() {
|
||||
setDeleteOpen(false);
|
||||
setDeletingNode(null);
|
||||
await refreshNodes();
|
||||
} catch (error: any) {
|
||||
toast.error(error.message || 'Failed to delete node');
|
||||
} catch (error) {
|
||||
toast.error((error as Error).message || 'Failed to delete node');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -153,8 +153,8 @@ export function NodeManager() {
|
||||
toast.error(`Failed to connect: ${result.error}`);
|
||||
}
|
||||
await refreshNodes();
|
||||
} catch (error: any) {
|
||||
toast.error(error.message || 'Connection test failed');
|
||||
} catch (error) {
|
||||
toast.error((error as Error).message || 'Connection test failed');
|
||||
} finally {
|
||||
setTesting(null);
|
||||
}
|
||||
@@ -169,8 +169,8 @@ export function NodeManager() {
|
||||
const { token } = await res.json();
|
||||
setGeneratedToken(token);
|
||||
toast.success('Node token generated');
|
||||
} catch (error: any) {
|
||||
toast.error(error.message || 'Failed to generate token');
|
||||
} catch (error) {
|
||||
toast.error((error as Error).message || 'Failed to generate token');
|
||||
} finally {
|
||||
setGeneratingToken(false);
|
||||
}
|
||||
|
||||
@@ -78,8 +78,8 @@ export default function ResourcesView() {
|
||||
setNetworks(await networksRes.json());
|
||||
setOrphans(await orphansRes.json());
|
||||
setSelectedOrphans([]);
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch data', error);
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch data', err);
|
||||
toast.error('Failed to load resources data');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
@@ -105,7 +105,7 @@ export default function ResourcesView() {
|
||||
toast.success(`Pruned ${confirmPruneType}.`);
|
||||
}
|
||||
await fetchAllData();
|
||||
} catch (error) {
|
||||
} catch {
|
||||
toast.error(`Failed to prune ${confirmPruneType}`);
|
||||
} finally {
|
||||
setIsActioning(false);
|
||||
@@ -124,7 +124,7 @@ export default function ResourcesView() {
|
||||
if (!res.ok) throw new Error();
|
||||
toast.success(`Deleted ${confirmDelete.type.slice(0, -1)}`);
|
||||
await fetchAllData();
|
||||
} catch (error) {
|
||||
} catch {
|
||||
toast.error(`Failed to delete ${confirmDelete.type.slice(0, -1)}`);
|
||||
} finally {
|
||||
setIsActioning(false);
|
||||
@@ -154,7 +154,7 @@ export default function ResourcesView() {
|
||||
toast.success(`Purged ${selectedOrphans.length} ghost container(s)`);
|
||||
setBulkPurgeConfirm(false);
|
||||
await fetchAllData();
|
||||
} catch (error) {
|
||||
} catch {
|
||||
toast.error('Failed to purge selected containers.');
|
||||
} finally {
|
||||
setIsActioning(false);
|
||||
@@ -169,7 +169,7 @@ export default function ResourcesView() {
|
||||
|
||||
const totalReclaimable = chartData.reduce((acc, curr) => acc + curr.value, 0);
|
||||
|
||||
const CustomTooltip = ({ active, payload }: any) => {
|
||||
const CustomTooltip = ({ active, payload }: { active?: boolean; payload?: Array<{ name: string; value: number }> }) => {
|
||||
if (active && payload && payload.length) {
|
||||
return (
|
||||
<div className="bg-popover text-popover-foreground border rounded-lg shadow-md p-3 text-sm">
|
||||
|
||||
@@ -147,7 +147,7 @@ export function StackAlertSheet({ isOpen, onClose, stackName }: StackAlertSheetP
|
||||
const err = await res.json().catch(() => ({}));
|
||||
toast.error(err?.error || 'Failed to delete alert rule.');
|
||||
}
|
||||
} catch (e) {
|
||||
} catch {
|
||||
toast.error('Network error. Could not reach the node.');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable */
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable */
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable */
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
@@ -204,4 +205,4 @@ export {
|
||||
type DialogDescriptionProps,
|
||||
type DialogContextType,
|
||||
type DialogFlipDirection,
|
||||
};
|
||||
};
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable */
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
@@ -560,4 +561,4 @@ export {
|
||||
type DropdownMenuRadioGroupProps,
|
||||
type DropdownMenuContextType,
|
||||
type DropdownMenuSubContextType,
|
||||
};
|
||||
};
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable */
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
@@ -204,4 +205,4 @@ export {
|
||||
type HoverCardContentProps,
|
||||
type HoverCardArrowProps,
|
||||
type HoverCardContextType,
|
||||
};
|
||||
};
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable */
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
@@ -159,4 +160,4 @@ export {
|
||||
type PopoverCloseProps,
|
||||
type PopoverArrowProps,
|
||||
type PopoverContextType,
|
||||
};
|
||||
};
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable */
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
@@ -188,4 +189,4 @@ export {
|
||||
type SheetFooterProps,
|
||||
type SheetTitleProps,
|
||||
type SheetDescriptionProps,
|
||||
};
|
||||
};
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable */
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
@@ -152,4 +153,4 @@ export {
|
||||
type SwitchIconProps,
|
||||
type SwitchIconPosition,
|
||||
type SwitchContextType,
|
||||
};
|
||||
};
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable */
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
@@ -217,4 +218,4 @@ export {
|
||||
type TooltipContentProps,
|
||||
type TooltipArrowProps,
|
||||
type TooltipContextType,
|
||||
};
|
||||
};
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable */
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
@@ -350,4 +351,4 @@ function SlidingNumber({
|
||||
);
|
||||
}
|
||||
|
||||
export { SlidingNumber, type SlidingNumberProps };
|
||||
export { SlidingNumber, type SlidingNumberProps };
|
||||
@@ -33,4 +33,5 @@ function Badge({ className, variant, ...props }: BadgeProps) {
|
||||
)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-refresh/only-export-components
|
||||
export { Badge, badgeVariants }
|
||||
|
||||
@@ -54,4 +54,5 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
)
|
||||
Button.displayName = "Button"
|
||||
|
||||
// eslint-disable-next-line react-refresh/only-export-components
|
||||
export { Button, buttonVariants }
|
||||
|
||||
@@ -108,6 +108,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-refresh/only-export-components
|
||||
export function useAuth() {
|
||||
const context = useContext(AuthContext);
|
||||
if (context === undefined) {
|
||||
|
||||
@@ -98,6 +98,7 @@ export function NodeProvider({ children }: { children: React.ReactNode }) {
|
||||
);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-refresh/only-export-components
|
||||
export function useNodes() {
|
||||
const context = useContext(NodeContext);
|
||||
if (!context) {
|
||||
|
||||
@@ -51,4 +51,5 @@ function useDataState<T extends HTMLElement = HTMLElement>(
|
||||
return [value, localRef];
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-refresh/only-export-components
|
||||
export { useDataState, type DataStateValue };
|
||||
|
||||
@@ -22,4 +22,5 @@ function useIsInView<T extends HTMLElement = HTMLElement>(
|
||||
return { ref: localRef, isInView };
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-refresh/only-export-components
|
||||
export { useIsInView, type UseIsInViewOptions };
|
||||
|
||||
@@ -39,7 +39,7 @@ export async function apiFetch(
|
||||
if (errData.error && errData.error.includes('not found') && errData.error.includes('Node')) {
|
||||
window.dispatchEvent(new Event('node-not-found'));
|
||||
}
|
||||
} catch (e) {
|
||||
} catch {
|
||||
// Ignore JSON parse errors, caller handles standard 404s
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user