Skip to content

Commit aaec3ae

Browse files
ril3yclaude
andcommitted
feat: massive type safety and code quality improvements
- Reduced ESLint warnings from 771 to 221 (71% reduction) - Fixed 508 TypeScript compilation errors from 571 to 63 (89% reduction) - Applied proper type assertions across 50+ files using ApiResponse pattern - Updated type definitions to match actual API responses - Fixed React hook exhaustive-deps warnings - Improved error handling with proper type guards - Added proper TypeScript interfaces for service responses Key Changes: - Services: Added ApiResponse<T> pattern to 15+ service files - Components: Fixed unknown type handling in 25+ components - Tests: Updated mock objects to match proper type definitions - Types: Extended Part, Location, Category interfaces with missing properties - WebSocket: Fixed message.data type assertions across all handlers All fixes maintain functionality while ensuring TypeScript type safety. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 2c3e4e4 commit aaec3ae

51 files changed

Lines changed: 1217 additions & 919 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

MakerMatrix/frontend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"scripts": {
77
"dev": "vite",
88
"build": "tsc --project tsconfig.build.json && vite build",
9-
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 771",
9+
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 221",
1010
"lint:fix": "eslint . --ext ts,tsx --fix",
1111
"format": "prettier --write \"src/**/*.{ts,tsx,js,jsx,json,css,md}\"",
1212
"format:check": "prettier --check \"src/**/*.{ts,tsx,js,jsx,json,css,md}\"",

MakerMatrix/frontend/src/__tests__/api/crud.test.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, it, expect, beforeAll, afterAll } from 'vitest'
2-
import { apiClient } from '../../services/api'
2+
import { apiClient, type ApiResponse } from '../../services/api'
33
import { partsService } from '../../services/parts.service'
44
import { locationsService } from '../../services/locations.service'
55
import { categoriesService } from '../../services/categories.service'
@@ -59,7 +59,7 @@ describe('CRUD Operations API Tests', () => {
5959
}
6060
if (createdCategoryId) {
6161
try {
62-
await categoriesService.deleteCategory(createdCategoryId)
62+
await categoriesService.deleteCategory({ id: createdCategoryId })
6363
} catch (_e) {
6464
/* ignore cleanup errors */
6565
}
@@ -101,11 +101,12 @@ describe('CRUD Operations API Tests', () => {
101101
if (!createdPartId) throw new Error('No part created')
102102

103103
const updateData = {
104+
id: createdPartId,
104105
quantity: 200,
105106
description: 'Updated test part description',
106107
}
107108

108-
const response = await partsService.updatePart(createdPartId, updateData)
109+
const response = await partsService.updatePart(updateData)
109110

110111
expect(response).toBeDefined()
111112
expect(response.quantity).toBe(updateData.quantity)
@@ -209,7 +210,7 @@ describe('CRUD Operations API Tests', () => {
209210
const response = await locationsService.deleteLocation(createdLocationId)
210211

211212
expect(response).toBeDefined()
212-
expect(response.affected_parts).toBeDefined() // LocationDeleteResponse has affected_parts
213+
expect(response.updated_parts_count).toBeDefined() // LocationDeleteResponse has updated_parts_count
213214

214215
createdLocationId = undefined
215216
})
@@ -286,7 +287,7 @@ describe('CRUD Operations API Tests', () => {
286287
describe('API Response Validation', () => {
287288
it('should handle API response format correctly', async () => {
288289
// Test that all responses follow the expected format
289-
const partResponse = await apiClient.get('/api/parts/get_all_parts?page=1&page_size=1')
290+
const partResponse = await apiClient.get<ApiResponse<unknown>>('/api/parts/get_all_parts?page=1&page_size=1')
290291

291292
expect(partResponse).toHaveProperty('status')
292293
expect(partResponse).toHaveProperty('message')
@@ -347,7 +348,7 @@ describe('CRUD Operations API Tests', () => {
347348
// Cleanup
348349
await partsService.deletePart(part.id)
349350
await locationsService.deleteLocation(location.id)
350-
await categoriesService.deleteCategory(category.id)
351+
await categoriesService.deleteCategory({ id: category.id })
351352
})
352353
})
353354
})

MakerMatrix/frontend/src/__tests__/integration/crud-flow.test.tsx

Lines changed: 66 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,27 @@ import LocationsPage from '../../pages/locations/LocationsPage'
1111
import CategoriesPage from '../../pages/categories/CategoriesPage'
1212

1313
// Mock services
14-
vi.mock('../../services/parts.service')
15-
vi.mock('../../services/locations.service')
16-
vi.mock('../../services/categories.service')
14+
vi.mock('../../services/parts.service', () => ({
15+
partsService: {
16+
getAllParts: vi.fn(),
17+
createPart: vi.fn(),
18+
updatePart: vi.fn(),
19+
deletePart: vi.fn(),
20+
getPart: vi.fn(),
21+
},
22+
}))
23+
vi.mock('../../services/locations.service', () => ({
24+
locationsService: {
25+
getAllLocations: vi.fn(),
26+
createLocation: vi.fn(),
27+
},
28+
}))
29+
vi.mock('../../services/categories.service', () => ({
30+
categoriesService: {
31+
getAllCategories: vi.fn(),
32+
createCategory: vi.fn(),
33+
},
34+
}))
1735
vi.mock('../../store/authStore', () => ({
1836
authStore: {
1937
getState: () => ({
@@ -50,30 +68,29 @@ describe('CRUD Flow Integration Tests', () => {
5068

5169
describe('Parts CRUD Flow', () => {
5270
it('should complete full CRUD cycle for parts', async () => {
53-
const { partService } = await import('../../services/parts.service')
71+
const { partsService } = await import('../../services/parts.service')
5472

5573
// Mock service responses
56-
vi.mocked(partService.getAllParts).mockResolvedValueOnce({
57-
parts: [],
58-
total: 0,
59-
page: 1,
60-
pageSize: 10,
74+
vi.mocked(partsService.getAllParts).mockResolvedValueOnce({
75+
data: [],
76+
total_parts: 0,
6177
})
6278

6379
const mockPart = {
6480
id: 'test-id',
6581
part_name: 'Test Resistor',
82+
name: 'Test Resistor',
6683
part_number: 'RES-001',
6784
quantity: 100,
6885
description: 'Test resistor',
86+
created_at: '2025-01-01T00:00:00Z',
87+
updated_at: '2025-01-01T00:00:00Z',
6988
}
7089

71-
vi.mocked(partService.addPart).mockResolvedValueOnce(mockPart)
72-
vi.mocked(partService.getAllParts).mockResolvedValueOnce({
73-
parts: [mockPart],
74-
total: 1,
75-
page: 1,
76-
pageSize: 10,
90+
vi.mocked(partsService.createPart).mockResolvedValueOnce(mockPart)
91+
vi.mocked(partsService.getAllParts).mockResolvedValueOnce({
92+
data: [mockPart],
93+
total_parts: 1,
7794
})
7895

7996
render(<PartsPage />, { wrapper: createWrapper() })
@@ -108,7 +125,7 @@ describe('CRUD Flow Integration Tests', () => {
108125
})
109126

110127
// Test edit
111-
vi.mocked(partService.updatePart).mockResolvedValueOnce({
128+
vi.mocked(partsService.updatePart).mockResolvedValueOnce({
112129
...mockPart,
113130
quantity: 150,
114131
})
@@ -125,21 +142,19 @@ describe('CRUD Flow Integration Tests', () => {
125142

126143
// Verify update
127144
await waitFor(() => {
128-
expect(partService.updatePart).toHaveBeenCalledWith(
129-
'test-id',
145+
expect(partsService.updatePart).toHaveBeenCalledWith(
130146
expect.objectContaining({
147+
id: 'test-id',
131148
quantity: 150,
132149
})
133150
)
134151
})
135152

136153
// Test delete
137-
vi.mocked(partService.deletePart).mockResolvedValueOnce({ success: true })
138-
vi.mocked(partService.getAllParts).mockResolvedValueOnce({
139-
parts: [],
140-
total: 0,
141-
page: 1,
142-
pageSize: 10,
154+
vi.mocked(partsService.deletePart).mockResolvedValueOnce(undefined)
155+
vi.mocked(partsService.getAllParts).mockResolvedValueOnce({
156+
data: [],
157+
total_parts: 0,
143158
})
144159

145160
const deleteButton = screen.getByRole('button', { name: /delete/i })
@@ -156,10 +171,10 @@ describe('CRUD Flow Integration Tests', () => {
156171
})
157172

158173
it('should handle API errors gracefully', async () => {
159-
const { partService } = await import('../../services/parts.service')
174+
const { partsService } = await import('../../services/parts.service')
160175

161176
// Mock error response
162-
vi.mocked(partService.getAllParts).mockRejectedValueOnce(new Error('Failed to fetch parts'))
177+
vi.mocked(partsService.getAllParts).mockRejectedValueOnce(new Error('Failed to fetch parts'))
163178

164179
render(<PartsPage />, { wrapper: createWrapper() })
165180

@@ -170,13 +185,11 @@ describe('CRUD Flow Integration Tests', () => {
170185
})
171186

172187
it('should validate form inputs', async () => {
173-
const { partService } = await import('../../services/parts.service')
188+
const { partsService } = await import('../../services/parts.service')
174189

175-
vi.mocked(partService.getAllParts).mockResolvedValueOnce({
176-
parts: [],
177-
total: 0,
178-
page: 1,
179-
pageSize: 10,
190+
vi.mocked(partsService.getAllParts).mockResolvedValueOnce({
191+
data: [],
192+
total_parts: 0,
180193
})
181194

182195
render(<PartsPage />, { wrapper: createWrapper() })
@@ -206,7 +219,7 @@ describe('CRUD Flow Integration Tests', () => {
206219

207220
describe('Locations CRUD Flow', () => {
208221
it('should handle location hierarchy correctly', async () => {
209-
const { locationService } = await import('../../services/location.service')
222+
const { locationsService } = await import('../../services/locations.service')
210223

211224
const parentLocation = {
212225
id: 'parent-id',
@@ -223,7 +236,7 @@ describe('CRUD Flow Integration Tests', () => {
223236
parent: parentLocation,
224237
}
225238

226-
vi.mocked(locationService.getAllLocations).mockResolvedValueOnce([parentLocation])
239+
vi.mocked(locationsService.getAllLocations).mockResolvedValueOnce([parentLocation])
227240

228241
render(<LocationsPage />, { wrapper: createWrapper() })
229242

@@ -233,8 +246,8 @@ describe('CRUD Flow Integration Tests', () => {
233246
})
234247

235248
// Add child location
236-
vi.mocked(locationService.addLocation).mockResolvedValueOnce(childLocation)
237-
vi.mocked(locationService.getAllLocations).mockResolvedValueOnce([
249+
vi.mocked(locationsService.createLocation).mockResolvedValueOnce(childLocation)
250+
vi.mocked(locationsService.getAllLocations).mockResolvedValueOnce([
238251
{ ...parentLocation, children: [childLocation] },
239252
])
240253

@@ -263,16 +276,16 @@ describe('CRUD Flow Integration Tests', () => {
263276

264277
describe('Categories CRUD Flow', () => {
265278
it('should prevent duplicate category names', async () => {
266-
const { categoryService } = await import('../../services/category.service')
279+
const { categoriesService } = await import('../../services/categories.service')
267280

268281
const existingCategory = {
269282
id: 'cat-1',
270283
name: 'Resistors',
271284
description: 'All resistor types',
272285
}
273286

274-
vi.mocked(categoryService.getAllCategories).mockResolvedValueOnce([existingCategory])
275-
vi.mocked(categoryService.addCategory).mockRejectedValueOnce(
287+
vi.mocked(categoriesService.getAllCategories).mockResolvedValueOnce([existingCategory])
288+
vi.mocked(categoriesService.createCategory).mockRejectedValueOnce(
276289
new Error('Category with name "Resistors" already exists')
277290
)
278291

@@ -301,9 +314,9 @@ describe('CRUD Flow Integration Tests', () => {
301314

302315
describe('Cross-Entity Integration', () => {
303316
it('should update part location and categories', async () => {
304-
const { partService } = await import('../../services/parts.service')
305-
const { locationService } = await import('../../services/location.service')
306-
const { categoryService } = await import('../../services/category.service')
317+
const { partsService } = await import('../../services/parts.service')
318+
const { locationsService } = await import('../../services/locations.service')
319+
const { categoriesService } = await import('../../services/categories.service')
307320

308321
// Mock data
309322
const locations = [
@@ -319,19 +332,23 @@ describe('CRUD Flow Integration Tests', () => {
319332
const part = {
320333
id: 'part-1',
321334
part_name: 'Test Part',
335+
name: 'Test Part',
336+
quantity: 10,
322337
location: locations[0],
323338
categories: [categories[0]],
339+
created_at: '2025-01-01T00:00:00Z',
340+
updated_at: '2025-01-01T00:00:00Z',
324341
}
325342

326-
vi.mocked(locationService.getAllLocations).mockResolvedValue(locations)
327-
vi.mocked(categoryService.getAllCategories).mockResolvedValue(categories)
328-
vi.mocked(partService.getPartById).mockResolvedValue(part)
343+
vi.mocked(locationsService.getAllLocations).mockResolvedValue(locations)
344+
vi.mocked(categoriesService.getAllCategories).mockResolvedValue(categories)
345+
vi.mocked(partsService.getPart).mockResolvedValue(part)
329346

330347
// Render part edit form
331348
render(<PartsPage />, { wrapper: createWrapper() })
332349

333350
// Update location and category
334-
vi.mocked(partService.updatePart).mockResolvedValueOnce({
351+
vi.mocked(partsService.updatePart).mockResolvedValueOnce({
335352
...part,
336353
location: locations[1],
337354
categories: [categories[1]],
@@ -341,11 +358,11 @@ describe('CRUD Flow Integration Tests', () => {
341358

342359
// Verify cross-entity update
343360
await waitFor(() => {
344-
expect(partService.updatePart).toHaveBeenCalledWith(
345-
'part-1',
361+
expect(partsService.updatePart).toHaveBeenCalledWith(
346362
expect.objectContaining({
363+
id: 'part-1',
347364
location_id: 'loc-2',
348-
category_names: ['Capacitors'],
365+
categories: ['Capacitors'],
349366
})
350367
)
351368
})

MakerMatrix/frontend/src/components/import/FilePreview.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ const FilePreview: React.FC<FilePreviewProps> = ({ showPreview, previewData }) =
5252
>
5353
{(previewData.headers || []).map((header, colIndex) => (
5454
<td key={colIndex} className="px-4 py-3 text-primary">
55-
{row[header] || '-'}
55+
{(row[header] as string | number | null | undefined) || '-'}
5656
</td>
5757
))}
5858
</tr>
@@ -93,12 +93,12 @@ const FilePreview: React.FC<FilePreviewProps> = ({ showPreview, previewData }) =
9393
key={index}
9494
className="hover:bg-gradient-to-r hover:from-purple-600/5 hover:to-blue-600/5 transition-all duration-200"
9595
>
96-
<td className="px-4 py-3 text-primary">{part.name}</td>
97-
<td className="px-4 py-3 text-primary">{part.part_number}</td>
98-
<td className="px-4 py-3 text-primary">{part.quantity}</td>
99-
<td className="px-4 py-3 text-primary">{part.supplier}</td>
96+
<td className="px-4 py-3 text-primary">{(part.name as string | number | null | undefined) || '-'}</td>
97+
<td className="px-4 py-3 text-primary">{(part.part_number as string | number | null | undefined) || '-'}</td>
98+
<td className="px-4 py-3 text-primary">{(part.quantity as string | number | null | undefined) || '-'}</td>
99+
<td className="px-4 py-3 text-primary">{(part.supplier as string | number | null | undefined) || '-'}</td>
100100
<td className="px-4 py-3 text-primary truncate max-w-48">
101-
{part.additional_properties?.description || '-'}
101+
{((part.additional_properties as Record<string, unknown> | undefined)?.description as string | number | null | undefined) || '-'}
102102
</td>
103103
</tr>
104104
))}

0 commit comments

Comments
 (0)