!isCreating && setShowCreateModal(false)}
+ onClose={() => {
+ if (!isCreating) {
+ setShowCreateModal(false);
+ }
+ }}
title="Create New Team"
>
diff --git a/apps/web/src/components/auth/LogoutButton.tsx b/apps/web/src/components/auth/LogoutButton.tsx
index 84dbcca..67ee894 100644
--- a/apps/web/src/components/auth/LogoutButton.tsx
+++ b/apps/web/src/components/auth/LogoutButton.tsx
@@ -16,7 +16,7 @@ export function LogoutButton({ variant = "secondary", className }: LogoutButtonP
const handleSignOut = async () => {
try {
await signOut();
- } catch (_error) {
+ } catch (error) {
console.error("Sign out error:", error);
} finally {
router.push("/login");
diff --git a/apps/web/src/components/chat/BackendStatusBanner.tsx b/apps/web/src/components/chat/BackendStatusBanner.tsx
index c3a5f34..ed06099 100644
--- a/apps/web/src/components/chat/BackendStatusBanner.tsx
+++ b/apps/web/src/components/chat/BackendStatusBanner.tsx
@@ -25,7 +25,7 @@ export function BackendStatusBanner() {
try {
// NOTE: Implement signOut (see issue #TBD)
// await signOut();
- } catch (_error) {
+ } catch (error) {
// Silently fail - will redirect anyway
void error;
}
@@ -63,8 +63,8 @@ export function BackendStatusBanner() {
/>
- {error || "Backend temporarily unavailable."}
- {retryIn > 0 && Retrying in {retryIn}s...}
+ {_error || "Backend temporarily unavailable."}
+ {_retryIn > 0 && Retrying in {_retryIn}s...}
diff --git a/apps/web/src/components/chat/MessageList.tsx b/apps/web/src/components/chat/MessageList.tsx
index 61f4565..023cf03 100644
--- a/apps/web/src/components/chat/MessageList.tsx
+++ b/apps/web/src/components/chat/MessageList.tsx
@@ -122,7 +122,7 @@ function MessageBubble({ message }: { message: Message }) {
backgroundColor: "rgb(var(--surface-2))",
color: "rgb(var(--text-muted))",
}}
- title={`Prompt: ${message.promptTokens?.toLocaleString() || 0} tokens, Completion: ${message.completionTokens?.toLocaleString() || 0} tokens`}
+ title={`Prompt: ${message.promptTokens?.toLocaleString() || "0"} tokens, Completion: ${message.completionTokens?.toLocaleString() || "0"} tokens`}
>
{formatTokenCount(message.totalTokens)} tokens
diff --git a/apps/web/src/components/dashboard/QuickCaptureWidget.tsx b/apps/web/src/components/dashboard/QuickCaptureWidget.tsx
index 441b3b0..77079c2 100644
--- a/apps/web/src/components/dashboard/QuickCaptureWidget.tsx
+++ b/apps/web/src/components/dashboard/QuickCaptureWidget.tsx
@@ -8,7 +8,7 @@ export function QuickCaptureWidget() {
const [idea, setIdea] = useState("");
const router = useRouter();
- const handleSubmit = (e: React.FormEvent) => {
+ const handleSubmit = (e: React.SyntheticEvent
) => {
e.preventDefault();
if (!idea.trim()) return;
diff --git a/apps/web/src/components/domains/DomainSelector.test.tsx b/apps/web/src/components/domains/DomainSelector.test.tsx
index fa577ec..a048e57 100644
--- a/apps/web/src/components/domains/DomainSelector.test.tsx
+++ b/apps/web/src/components/domains/DomainSelector.test.tsx
@@ -88,7 +88,7 @@ describe("DomainSelector", (): void => {
const onChange = vi.fn();
render();
- const select = screen.getByRole("combobox");
+ const select = screen.getByRole("combobox") as HTMLSelectElement;
expect(select.value).toBe("domain-1");
});
diff --git a/apps/web/src/components/filters/FilterBar.tsx b/apps/web/src/components/filters/FilterBar.tsx
index d1158c6..46d2d9c 100644
--- a/apps/web/src/components/filters/FilterBar.tsx
+++ b/apps/web/src/components/filters/FilterBar.tsx
@@ -50,7 +50,7 @@ export function FilterBar({
}, [searchValue, debounceMs]);
const handleFilterChange = useCallback(
- (key: keyof FilterValues, value: any) => {
+ (key: keyof FilterValues, value: FilterValues[keyof FilterValues]) => {
const newFilters = { ...filters, [key]: value };
if (!value || (Array.isArray(value) && value.length === 0)) {
delete newFilters[key];
diff --git a/apps/web/src/components/hud/WidgetGrid.tsx b/apps/web/src/components/hud/WidgetGrid.tsx
index 5571f90..0d81084 100644
--- a/apps/web/src/components/hud/WidgetGrid.tsx
+++ b/apps/web/src/components/hud/WidgetGrid.tsx
@@ -87,9 +87,9 @@ export function WidgetGrid({
return layoutItem;
});
- const handleLayoutChange = (layout: readonly any[]) => {
+ const handleLayoutChange = (layout: readonly WidgetPlacement[]) => {
if (onLayoutChange) {
- onLayoutChange([...layout] as WidgetPlacement[]);
+ onLayoutChange(layout);
}
};
diff --git a/apps/web/src/components/kanban/KanbanBoard.test.tsx b/apps/web/src/components/kanban/KanbanBoard.test.tsx
index 51da262..ae2f5f9 100644
--- a/apps/web/src/components/kanban/KanbanBoard.test.tsx
+++ b/apps/web/src/components/kanban/KanbanBoard.test.tsx
@@ -226,8 +226,8 @@ describe("KanbanBoard", (): void => {
const fetchMock = global.fetch as ReturnType;
fetchMock.mockResolvedValueOnce({
ok: true,
- json: () => ({ status: TaskStatus.IN_PROGRESS }),
- } as Response);
+ json: () => Promise.resolve({ status: TaskStatus.IN_PROGRESS }),
+ } as unknown as Response);
render();
diff --git a/apps/web/src/components/kanban/KanbanBoard.tsx b/apps/web/src/components/kanban/KanbanBoard.tsx
index 18d93c5..ce5e516 100644
--- a/apps/web/src/components/kanban/KanbanBoard.tsx
+++ b/apps/web/src/components/kanban/KanbanBoard.tsx
@@ -110,7 +110,7 @@ export function KanbanBoard({ tasks, onStatusChange }: KanbanBoardProps): React.
if (onStatusChange) {
onStatusChange(taskId, newStatus);
}
- } catch (_error) {
+ } catch (error) {
console.error("Error updating task status:", error);
// TODO: Show error toast/notification
}
diff --git a/apps/web/src/components/knowledge/ImportExportActions.tsx b/apps/web/src/components/knowledge/ImportExportActions.tsx
index 4792f76..77c167f 100644
--- a/apps/web/src/components/knowledge/ImportExportActions.tsx
+++ b/apps/web/src/components/knowledge/ImportExportActions.tsx
@@ -80,7 +80,7 @@ export function ImportExportActions({
if (result.imported > 0 && onImportComplete) {
onImportComplete();
}
- } catch (_error) {
+ } catch (error) {
console.error("Import error:", error);
alert(error instanceof Error ? error.message : "Failed to import file");
setShowImportDialog(false);
@@ -135,7 +135,7 @@ export function ImportExportActions({
a.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
- } catch (_error) {
+ } catch (error) {
console.error("Export error:", error);
alert("Failed to export entries");
} finally {
diff --git a/apps/web/src/components/knowledge/LinkAutocomplete.tsx b/apps/web/src/components/knowledge/LinkAutocomplete.tsx
index 2c81ae6..e4a94cb 100644
--- a/apps/web/src/components/knowledge/LinkAutocomplete.tsx
+++ b/apps/web/src/components/knowledge/LinkAutocomplete.tsx
@@ -8,7 +8,7 @@ interface LinkAutocompleteProps {
/**
* The textarea element to attach autocomplete to
*/
- textareaRef: React.RefObject;
+ textareaRef: React.RefObject;
/**
* Callback when a link is selected
*/
@@ -82,7 +82,7 @@ export function LinkAutocomplete({
setResults(searchResults);
setSelectedIndex(0);
- } catch (_error) {
+ } catch (error) {
console.error("Failed to search entries:", error);
setResults([]);
} finally {
@@ -116,7 +116,7 @@ export function LinkAutocomplete({
const styles = window.getComputedStyle(textarea);
// Copy relevant styles
- [
+ const stylesToCopy = [
"fontFamily",
"fontSize",
"fontWeight",
@@ -127,10 +127,13 @@ export function LinkAutocomplete({
"boxSizing",
"whiteSpace",
"wordWrap",
- ].forEach((prop) => {
- mirror.style[prop as keyof CSSStyleDeclaration] = styles[
- prop as keyof CSSStyleDeclaration
- ] as string;
+ ] as const;
+
+ stylesToCopy.forEach((prop) => {
+ const value = styles.getPropertyValue(prop);
+ if (value) {
+ mirror.style.setProperty(prop, value);
+ }
});
mirror.style.position = "absolute";
diff --git a/apps/web/src/components/knowledge/__tests__/EntryEditor.test.tsx b/apps/web/src/components/knowledge/__tests__/EntryEditor.test.tsx
index 8b20970..3e9d537 100644
--- a/apps/web/src/components/knowledge/__tests__/EntryEditor.test.tsx
+++ b/apps/web/src/components/knowledge/__tests__/EntryEditor.test.tsx
@@ -31,7 +31,7 @@ describe("EntryEditor", (): void => {
const content = "# Test Content\n\nThis is a test.";
render();
- const textarea = screen.getByPlaceholderText(/Write your content here/);
+ const textarea = screen.getByPlaceholderText(/Write your content here/) as HTMLTextAreaElement;
expect(textarea.value).toBe(content);
});
@@ -112,7 +112,7 @@ describe("EntryEditor", (): void => {
render();
// Verify content in edit mode
- const textarea = screen.getByPlaceholderText(/Write your content here/);
+ const textarea = screen.getByPlaceholderText(/Write your content here/) as HTMLTextAreaElement;
expect(textarea.value).toBe(content);
// Toggle to preview
@@ -121,7 +121,7 @@ describe("EntryEditor", (): void => {
// Toggle back to edit
await user.click(screen.getByText("Edit"));
- const textareaAfter = screen.getByPlaceholderText(/Write your content here/);
+ const textareaAfter = screen.getByPlaceholderText(/Write your content here/) as HTMLTextAreaElement;
expect(textareaAfter.value).toBe(content);
});
diff --git a/apps/web/src/components/knowledge/__tests__/LinkAutocomplete.test.tsx b/apps/web/src/components/knowledge/__tests__/LinkAutocomplete.test.tsx
index f32d1a3..605218f 100644
--- a/apps/web/src/components/knowledge/__tests__/LinkAutocomplete.test.tsx
+++ b/apps/web/src/components/knowledge/__tests__/LinkAutocomplete.test.tsx
@@ -380,7 +380,12 @@ describe("LinkAutocomplete", (): void => {
const searchPromise = new Promise((resolve) => {
resolveSearch = resolve;
});
- mockApiGet.mockReturnValue(searchPromise as Promise);
+ mockApiGet.mockReturnValue(
+ searchPromise as Promise<{
+ data: unknown[];
+ meta: { total: number; page: number; limit: number; totalPages: number };
+ }>
+ );
render();
diff --git a/apps/web/src/components/knowledge/index.ts b/apps/web/src/components/knowledge/index.ts
index a6a0929..f97bfec 100644
--- a/apps/web/src/components/knowledge/index.ts
+++ b/apps/web/src/components/knowledge/index.ts
@@ -7,3 +7,4 @@ export { EntryEditor } from "./EntryEditor";
export { EntryMetadata } from "./EntryMetadata";
export { VersionHistory } from "./VersionHistory";
export { ImportExportActions } from "./ImportExportActions";
+export { StatsDashboard } from "./StatsDashboard";
diff --git a/apps/web/src/components/mindmap/controls/ExportButton.tsx b/apps/web/src/components/mindmap/controls/ExportButton.tsx
index b0d470e..9d9a3c4 100644
--- a/apps/web/src/components/mindmap/controls/ExportButton.tsx
+++ b/apps/web/src/components/mindmap/controls/ExportButton.tsx
@@ -123,9 +123,10 @@ export function ExportButton({ graph, mermaid }: ExportButtonProps) {
case "mermaid":
exportAsMermaid();
break;
- case "png":
+ case "png": {
await exportAsPng();
break;
+ }
case "svg":
exportAsSvg();
break;
diff --git a/apps/web/src/components/mindmap/controls/NodeCreateModal.tsx b/apps/web/src/components/mindmap/controls/NodeCreateModal.tsx
index 6eff98e..a460f50 100644
--- a/apps/web/src/components/mindmap/controls/NodeCreateModal.tsx
+++ b/apps/web/src/components/mindmap/controls/NodeCreateModal.tsx
@@ -26,13 +26,13 @@ export function NodeCreateModal({ onClose, onCreate }: NodeCreateModalProps) {
const [domain, setDomain] = useState("");
const [isSubmitting, setIsSubmitting] = useState(false);
- const handleSubmit = async (e: React.FormEvent) => {
+ const handleSubmit = async (e: React.SyntheticEvent) => {
e.preventDefault();
if (!title.trim()) return;
setIsSubmitting(true);
try {
- await onCreate({
+ const result = await onCreate({
title: title.trim(),
node_type: nodeType,
content: content.trim() || null,
@@ -43,6 +43,7 @@ export function NodeCreateModal({ onClose, onCreate }: NodeCreateModalProps) {
domain: domain.trim() || null,
metadata: {},
});
+ return result;
} finally {
setIsSubmitting(false);
}
diff --git a/apps/web/src/components/mindmap/hooks/useGraphData.ts b/apps/web/src/components/mindmap/hooks/useGraphData.ts
index 6b1ed76..892fc44 100644
--- a/apps/web/src/components/mindmap/hooks/useGraphData.ts
+++ b/apps/web/src/components/mindmap/hooks/useGraphData.ts
@@ -284,7 +284,7 @@ export function useGraphData(options: UseGraphDataOptions = {}): UseGraphDataRes
}, [accessToken]);
const fetchMermaid = useCallback(
- (style: "flowchart" | "mindmap" = "flowchart"): void => {
+ async (style: "flowchart" | "mindmap" = "flowchart"): Promise => {
if (!graph) {
setError("No graph data available");
return;
@@ -356,7 +356,7 @@ export function useGraphData(options: UseGraphDataOptions = {}): UseGraphDataRes
[graph]
);
- const fetchStatistics = useCallback((): void => {
+ const fetchStatistics = useCallback(async (): Promise => {
if (!graph) return;
try {
@@ -577,7 +577,7 @@ export function useGraphData(options: UseGraphDataOptions = {}): UseGraphDataRes
// Update statistics when graph changes
useEffect(() => {
if (graph) {
- void fetchStatistics();
+ fetchStatistics();
}
}, [graph, fetchStatistics]);
diff --git a/apps/web/src/components/personalities/PersonalityForm.tsx b/apps/web/src/components/personalities/PersonalityForm.tsx
index cc92519..aad5ff2 100644
--- a/apps/web/src/components/personalities/PersonalityForm.tsx
+++ b/apps/web/src/components/personalities/PersonalityForm.tsx
@@ -56,7 +56,7 @@ export function PersonalityForm({
});
const [isSubmitting, setIsSubmitting] = useState(false);
- async function handleSubmit(e: React.FormEvent): Promise {
+ async function handleSubmit(e: React.SyntheticEvent): Promise {
e.preventDefault();
setIsSubmitting(true);
try {
diff --git a/apps/web/src/components/team/TeamMemberList.tsx b/apps/web/src/components/team/TeamMemberList.tsx
index f906d87..5446268 100644
--- a/apps/web/src/components/team/TeamMemberList.tsx
+++ b/apps/web/src/components/team/TeamMemberList.tsx
@@ -41,7 +41,7 @@ export function TeamMemberList({
await onAddMember(selectedUserId, selectedRole);
setSelectedUserId("");
setSelectedRole(TeamMemberRole.MEMBER);
- } catch (_error) {
+ } catch (error) {
console.error("Failed to add member:", error);
alert("Failed to add member. Please try again.");
} finally {
@@ -53,7 +53,7 @@ export function TeamMemberList({
setRemovingUserId(userId);
try {
await onRemoveMember(userId);
- } catch (_error) {
+ } catch (error) {
console.error("Failed to remove member:", error);
alert("Failed to remove member. Please try again.");
} finally {
diff --git a/apps/web/src/components/team/TeamSettings.tsx b/apps/web/src/components/team/TeamSettings.tsx
index e032ba6..53471de 100644
--- a/apps/web/src/components/team/TeamSettings.tsx
+++ b/apps/web/src/components/team/TeamSettings.tsx
@@ -34,7 +34,7 @@ export function TeamSettings({ team, onUpdate, onDelete }: TeamSettingsProps) {
}
await onUpdate(updates);
setIsEditing(false);
- } catch (_error) {
+ } catch (error) {
console.error("Failed to update team:", error);
alert("Failed to update team. Please try again.");
} finally {
@@ -52,7 +52,7 @@ export function TeamSettings({ team, onUpdate, onDelete }: TeamSettingsProps) {
setIsDeleting(true);
try {
await onDelete();
- } catch (_error) {
+ } catch (error) {
console.error("Failed to delete team:", error);
alert("Failed to delete team. Please try again.");
setIsDeleting(false);
diff --git a/apps/web/src/components/ui/select.tsx b/apps/web/src/components/ui/select.tsx
index 153a15c..810bbd8 100644
--- a/apps/web/src/components/ui/select.tsx
+++ b/apps/web/src/components/ui/select.tsx
@@ -32,7 +32,12 @@ const SelectContext = React.createContext<{
onValueChange?: (value: string) => void;
isOpen: boolean;
setIsOpen: (open: boolean) => void;
-}>({ isOpen: false, setIsOpen: () => {} });
+}>({
+ isOpen: false,
+ setIsOpen: () => {
+ // Default no-op
+ },
+});
export function Select({ value, onValueChange, defaultValue, disabled, children }: SelectProps) {
const [isOpen, setIsOpen] = React.useState(false);
diff --git a/apps/web/src/components/widgets/QuickCaptureWidget.tsx b/apps/web/src/components/widgets/QuickCaptureWidget.tsx
index 2db62f8..c933cda 100644
--- a/apps/web/src/components/widgets/QuickCaptureWidget.tsx
+++ b/apps/web/src/components/widgets/QuickCaptureWidget.tsx
@@ -11,7 +11,7 @@ export function QuickCaptureWidget({ id: _id, config: _config }: WidgetProps) {
const [isSubmitting, setIsSubmitting] = useState(false);
const [recentCaptures, setRecentCaptures] = useState([]);
- const handleSubmit = (e: React.FormEvent): void => {
+ const handleSubmit = (e: React.SyntheticEvent): void => {
e.preventDefault();
if (!input.trim() || isSubmitting) return;
diff --git a/apps/web/src/components/widgets/__tests__/CalendarWidget.test.tsx b/apps/web/src/components/widgets/__tests__/CalendarWidget.test.tsx
index 5c8f71f..04ceb70 100644
--- a/apps/web/src/components/widgets/__tests__/CalendarWidget.test.tsx
+++ b/apps/web/src/components/widgets/__tests__/CalendarWidget.test.tsx
@@ -15,9 +15,12 @@ describe("CalendarWidget", (): void => {
});
it("should render loading state initially", (): void => {
- vi.mocked(global.fetch).mockImplementation(() => new Promise(() => {
- // Intentionally never resolves to keep loading state
- }));
+ vi.mocked(global.fetch).mockImplementation(
+ () =>
+ new Promise(() => {
+ // Intentionally never resolves to keep loading state
+ })
+ );
render();
@@ -42,8 +45,8 @@ describe("CalendarWidget", (): void => {
vi.mocked(global.fetch).mockResolvedValueOnce({
ok: true,
- json: () => mockEvents,
- });
+ json: () => Promise.resolve(mockEvents),
+ } as unknown as Response);
render();
@@ -56,8 +59,8 @@ describe("CalendarWidget", (): void => {
it("should handle empty event list", async (): Promise => {
vi.mocked(global.fetch).mockResolvedValueOnce({
ok: true,
- json: () => [],
- });
+ json: () => Promise.resolve([]),
+ } as unknown as Response);
render();
@@ -91,8 +94,8 @@ describe("CalendarWidget", (): void => {
vi.mocked(global.fetch).mockResolvedValueOnce({
ok: true,
- json: () => mockEvents,
- });
+ json: () => Promise.resolve(mockEvents),
+ } as unknown as Response);
render();
@@ -105,8 +108,8 @@ describe("CalendarWidget", (): void => {
it("should display current date", async (): Promise => {
vi.mocked(global.fetch).mockResolvedValueOnce({
ok: true,
- json: () => [],
- });
+ json: () => Promise.resolve([]),
+ } as unknown as Response);
render();
diff --git a/apps/web/src/components/widgets/__tests__/QuickCaptureWidget.test.tsx b/apps/web/src/components/widgets/__tests__/QuickCaptureWidget.test.tsx
index e0457e1..b686975 100644
--- a/apps/web/src/components/widgets/__tests__/QuickCaptureWidget.test.tsx
+++ b/apps/web/src/components/widgets/__tests__/QuickCaptureWidget.test.tsx
@@ -41,8 +41,8 @@ describe("QuickCaptureWidget", (): void => {
const user = userEvent.setup();
vi.mocked(global.fetch).mockResolvedValueOnce({
ok: true,
- json: () => ({ success: true }),
- });
+ json: () => Promise.resolve({ success: true }),
+ } as unknown as Response);
render();
@@ -66,8 +66,8 @@ describe("QuickCaptureWidget", (): void => {
const user = userEvent.setup();
vi.mocked(global.fetch).mockResolvedValueOnce({
ok: true,
- json: () => ({ success: true }),
- });
+ json: () => Promise.resolve({ success: true }),
+ } as unknown as Response);
render();
@@ -113,8 +113,8 @@ describe("QuickCaptureWidget", (): void => {
const user = userEvent.setup();
vi.mocked(global.fetch).mockResolvedValueOnce({
ok: true,
- json: () => ({ success: true }),
- });
+ json: () => Promise.resolve({ success: true }),
+ } as unknown as Response);
render();
@@ -130,8 +130,8 @@ describe("QuickCaptureWidget", (): void => {
const user = userEvent.setup();
vi.mocked(global.fetch).mockResolvedValueOnce({
ok: true,
- json: () => ({ success: true }),
- });
+ json: () => Promise.resolve({ success: true }),
+ } as unknown as Response);
render();
diff --git a/apps/web/src/components/widgets/__tests__/TasksWidget.test.tsx b/apps/web/src/components/widgets/__tests__/TasksWidget.test.tsx
index 64469bd..0bd04a5 100644
--- a/apps/web/src/components/widgets/__tests__/TasksWidget.test.tsx
+++ b/apps/web/src/components/widgets/__tests__/TasksWidget.test.tsx
@@ -32,8 +32,8 @@ describe("TasksWidget", (): void => {
vi.mocked(global.fetch).mockResolvedValueOnce({
ok: true,
- json: () => mockTasks,
- });
+ json: () => Promise.resolve(mockTasks),
+ } as unknown as Response);
render();
@@ -52,8 +52,8 @@ describe("TasksWidget", (): void => {
vi.mocked(global.fetch).mockResolvedValueOnce({
ok: true,
- json: () => mockTasks,
- });
+ json: () => Promise.resolve(mockTasks),
+ } as unknown as Response);
render();
@@ -66,8 +66,8 @@ describe("TasksWidget", (): void => {
it("should handle empty task list", async (): Promise => {
vi.mocked(global.fetch).mockResolvedValueOnce({
ok: true,
- json: () => [],
- });
+ json: () => Promise.resolve([]),
+ } as unknown as Response);
render();
@@ -93,8 +93,8 @@ describe("TasksWidget", (): void => {
vi.mocked(global.fetch).mockResolvedValueOnce({
ok: true,
- json: () => mockTasks,
- });
+ json: () => Promise.resolve(mockTasks),
+ } as unknown as Response);
render();
@@ -114,8 +114,8 @@ describe("TasksWidget", (): void => {
vi.mocked(global.fetch).mockResolvedValueOnce({
ok: true,
- json: () => mockTasks,
- });
+ json: () => Promise.resolve(mockTasks),
+ } as unknown as Response);
render();
diff --git a/apps/web/src/components/widgets/__tests__/WidgetGrid.test.tsx b/apps/web/src/components/widgets/__tests__/WidgetGrid.test.tsx
index 1794d0b..cfaa923 100644
--- a/apps/web/src/components/widgets/__tests__/WidgetGrid.test.tsx
+++ b/apps/web/src/components/widgets/__tests__/WidgetGrid.test.tsx
@@ -10,8 +10,12 @@ import type { WidgetPlacement } from "@mosaic/shared";
// Mock react-grid-layout
vi.mock("react-grid-layout", () => ({
- default: ({ children }: any) => {children}
,
- Responsive: ({ children }: any) => {children}
,
+ default: ({ children }: { children: React.ReactNode }) => (
+ {children}
+ ),
+ Responsive: ({ children }: { children: React.ReactNode }) => (
+ {children}
+ ),
}));
describe("WidgetGrid", (): void => {
diff --git a/apps/web/src/components/workspace/InviteMember.tsx b/apps/web/src/components/workspace/InviteMember.tsx
index c3b1b40..bc9c9d3 100644
--- a/apps/web/src/components/workspace/InviteMember.tsx
+++ b/apps/web/src/components/workspace/InviteMember.tsx
@@ -13,7 +13,7 @@ export function InviteMember({ onInvite }: InviteMemberProps) {
const [isInviting, setIsInviting] = useState(false);
const [error, setError] = useState(null);
- const handleSubmit = async (e: React.FormEvent) => {
+ const handleSubmit = async (e: React.SyntheticEvent) => {
e.preventDefault();
setError(null);
@@ -33,7 +33,7 @@ export function InviteMember({ onInvite }: InviteMemberProps) {
setEmail("");
setRole(WorkspaceMemberRole.MEMBER);
alert("Invitation sent successfully!");
- } catch (_error) {
+ } catch (error) {
console.error("Failed to invite member:", error);
setError(
error instanceof Error ? error.message : "Failed to send invitation. Please try again."
diff --git a/apps/web/src/components/workspace/MemberList.tsx b/apps/web/src/components/workspace/MemberList.tsx
index cb1e815..8788399 100644
--- a/apps/web/src/components/workspace/MemberList.tsx
+++ b/apps/web/src/components/workspace/MemberList.tsx
@@ -37,7 +37,7 @@ export function MemberList({
const handleRoleChange = async (userId: string, newRole: WorkspaceMemberRole) => {
try {
await onRoleChange(userId, newRole);
- } catch (_error) {
+ } catch (error) {
console.error("Failed to change role:", error);
alert("Failed to change member role");
}
@@ -50,7 +50,7 @@ export function MemberList({
try {
await onRemove(userId);
- } catch (_error) {
+ } catch (error) {
console.error("Failed to remove member:", error);
alert("Failed to remove member");
}
diff --git a/apps/web/src/components/workspace/WorkspaceSettings.tsx b/apps/web/src/components/workspace/WorkspaceSettings.tsx
index 7a040b7..0c3c426 100644
--- a/apps/web/src/components/workspace/WorkspaceSettings.tsx
+++ b/apps/web/src/components/workspace/WorkspaceSettings.tsx
@@ -37,7 +37,7 @@ export function WorkspaceSettings({
try {
await onUpdate(name);
setIsEditing(false);
- } catch (_error) {
+ } catch (error) {
console.error("Failed to update workspace:", error);
alert("Failed to update workspace");
} finally {
@@ -49,7 +49,7 @@ export function WorkspaceSettings({
setIsDeleting(true);
try {
await onDelete();
- } catch (_error) {
+ } catch (error) {
console.error("Failed to delete workspace:", error);
alert("Failed to delete workspace");
setIsDeleting(false);
diff --git a/apps/web/src/hooks/__tests__/useLayouts.test.tsx b/apps/web/src/hooks/__tests__/useLayouts.test.tsx
index 2cd955b..997d84a 100644
--- a/apps/web/src/hooks/__tests__/useLayouts.test.tsx
+++ b/apps/web/src/hooks/__tests__/useLayouts.test.tsx
@@ -37,7 +37,7 @@ describe("useLayouts", (): void => {
{ id: "2", name: "Custom", isDefault: false, layout: [] },
];
- (global.fetch as any).mockResolvedValueOnce({
+ (global.fetch as ReturnType).mockResolvedValueOnce({
ok: true,
json: () => mockLayouts,
});
@@ -52,7 +52,7 @@ describe("useLayouts", (): void => {
});
it("should handle fetch errors", async (): Promise => {
- (global.fetch as any).mockRejectedValueOnce(new Error("API Error"));
+ (global.fetch as ReturnType).mockRejectedValueOnce(new Error("API Error"));
const { result } = renderHook(() => useLayouts(), {
wrapper: createWrapper(),
@@ -64,7 +64,7 @@ describe("useLayouts", (): void => {
});
it("should show loading state", (): void => {
- (global.fetch as any).mockImplementation(() => new Promise(() => {}));
+ (global.fetch as ReturnType).mockImplementation(() => new Promise(() => {}));
const { result } = renderHook(() => useLayouts(), {
wrapper: createWrapper(),
@@ -87,7 +87,7 @@ describe("useCreateLayout", (): void => {
layout: [],
};
- (global.fetch as any).mockResolvedValueOnce({
+ (global.fetch as ReturnType).mockResolvedValueOnce({
ok: true,
json: () => mockLayout,
});
@@ -108,7 +108,7 @@ describe("useCreateLayout", (): void => {
});
it("should handle creation errors", async (): Promise => {
- (global.fetch as any).mockRejectedValueOnce(new Error("API Error"));
+ (global.fetch as ReturnType).mockRejectedValueOnce(new Error("API Error"));
const { result } = renderHook(() => useCreateLayout(), {
wrapper: createWrapper(),
@@ -138,7 +138,7 @@ describe("useUpdateLayout", (): void => {
layout: [{ i: "widget-1", x: 0, y: 0, w: 2, h: 2 }],
};
- (global.fetch as any).mockResolvedValueOnce({
+ (global.fetch as ReturnType).mockResolvedValueOnce({
ok: true,
json: () => mockLayout,
});
@@ -160,7 +160,7 @@ describe("useUpdateLayout", (): void => {
});
it("should handle update errors", async (): Promise => {
- (global.fetch as any).mockRejectedValueOnce(new Error("API Error"));
+ (global.fetch as ReturnType).mockRejectedValueOnce(new Error("API Error"));
const { result } = renderHook(() => useUpdateLayout(), {
wrapper: createWrapper(),
@@ -183,7 +183,7 @@ describe("useDeleteLayout", (): void => {
});
it("should delete a layout", async (): Promise => {
- (global.fetch as any).mockResolvedValueOnce({
+ (global.fetch as ReturnType).mockResolvedValueOnce({
ok: true,
json: () => ({ success: true }),
});
@@ -200,7 +200,7 @@ describe("useDeleteLayout", (): void => {
});
it("should handle deletion errors", async (): Promise => {
- (global.fetch as any).mockRejectedValueOnce(new Error("API Error"));
+ (global.fetch as ReturnType).mockRejectedValueOnce(new Error("API Error"));
const { result } = renderHook(() => useDeleteLayout(), {
wrapper: createWrapper(),
diff --git a/apps/web/src/hooks/useWebSocket.test.tsx b/apps/web/src/hooks/useWebSocket.test.tsx
index 4abefa8..4de3290 100644
--- a/apps/web/src/hooks/useWebSocket.test.tsx
+++ b/apps/web/src/hooks/useWebSocket.test.tsx
@@ -17,14 +17,14 @@ describe("useWebSocket", (): void => {
mockSocket = {
on: vi.fn((event: string, handler: (...args: unknown[]) => void) => {
eventHandlers[event] = handler;
- return mockSocket as Socket;
+ return mockSocket;
}) as unknown as Socket["on"],
off: vi.fn((event?: string) => {
if (event && Object.hasOwn(eventHandlers, event)) {
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete eventHandlers[event];
}
- return mockSocket as Socket;
+ return mockSocket;
}) as unknown as Socket["off"],
connect: vi.fn(),
disconnect: vi.fn(),
diff --git a/apps/web/src/lib/api/client.ts b/apps/web/src/lib/api/client.ts
index f215e84..f1c2eb1 100644
--- a/apps/web/src/lib/api/client.ts
+++ b/apps/web/src/lib/api/client.ts
@@ -40,7 +40,7 @@ export async function apiRequest(endpoint: string, options: RequestInit = {})
(): ApiError => ({
code: "UNKNOWN_ERROR",
message: response.statusText || "An unknown error occurred",
- }),
+ })
);
throw new Error(error.message);
diff --git a/apps/web/src/lib/auth/auth-context.tsx b/apps/web/src/lib/auth/auth-context.tsx
index 850b68c..8d9c628 100644
--- a/apps/web/src/lib/auth/auth-context.tsx
+++ b/apps/web/src/lib/auth/auth-context.tsx
@@ -32,7 +32,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
const signOut = useCallback(async () => {
try {
await apiPost("/auth/sign-out");
- } catch (_error) {
+ } catch (error) {
console.error("Sign out error:", error);
} finally {
setUser(null);
diff --git a/apps/web/src/lib/hooks/useLayout.ts b/apps/web/src/lib/hooks/useLayout.ts
index 75adb29..aa3a6e0 100644
--- a/apps/web/src/lib/hooks/useLayout.ts
+++ b/apps/web/src/lib/hooks/useLayout.ts
@@ -35,7 +35,7 @@ export function useLayout() {
if (storedLayoutId) {
setCurrentLayoutId(storedLayoutId);
}
- } catch (_error) {
+ } catch (error) {
console.error("Failed to load layouts from localStorage:", error);
} finally {
setIsLoading(false);
@@ -48,7 +48,7 @@ export function useLayout() {
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(layouts));
localStorage.setItem(`${STORAGE_KEY}-current`, currentLayoutId);
- } catch (_error) {
+ } catch (error) {
console.error("Failed to save layouts to localStorage:", error);
}
}
@@ -215,7 +215,7 @@ export function useWorkspaceId(): string | null {
if (stored) {
setWorkspaceId(stored);
}
- } catch (_error) {
+ } catch (error) {
console.error("Failed to load workspace ID from localStorage:", error);
}
}, []);
diff --git a/apps/web/src/providers/ThemeProvider.tsx b/apps/web/src/providers/ThemeProvider.tsx
index c6eaa96..d189b4f 100644
--- a/apps/web/src/providers/ThemeProvider.tsx
+++ b/apps/web/src/providers/ThemeProvider.tsx
@@ -92,8 +92,12 @@ export function ThemeProvider({ children, defaultTheme = "system" }: ThemeProvid
value={{
theme: defaultTheme,
resolvedTheme: "dark",
- setTheme: (): void => {},
- toggleTheme: (): void => {},
+ setTheme: (): void => {
+ // No-op during SSR
+ },
+ toggleTheme: (): void => {
+ // No-op during SSR
+ },
}}
>
{children}
diff --git a/apps/web/src/test/setup.d.ts b/apps/web/src/test/setup.d.ts
new file mode 100644
index 0000000..9595cfc
--- /dev/null
+++ b/apps/web/src/test/setup.d.ts
@@ -0,0 +1,13 @@
+/**
+ * Type declarations for test environment
+ */
+
+import type { TestingLibraryMatchers } from "@testing-library/jest-dom/matchers";
+import type { Assertion, AsymmetricMatchersContaining } from "vitest";
+
+declare module "vitest" {
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type
+ interface Assertion extends TestingLibraryMatchers {}
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type
+ interface AsymmetricMatchersContaining extends TestingLibraryMatchers {}
+}
diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json
index 7770ae4..a0fc583 100644
--- a/apps/web/tsconfig.json
+++ b/apps/web/tsconfig.json
@@ -8,6 +8,6 @@
"noUnusedLocals": false,
"noUnusedParameters": false
},
- "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "src/test/setup.d.ts"],
"exclude": ["node_modules"]
}