feat: implement material validation UI in CleanerPage

New Features:
- Material validation from database (full table or filtered by ProductionID)
- Checkbox selection for materials to delete with auto-select for marked items
- Manager-based filtering (Admin only)
- Two-phase deletion: confirm (mark in DB) + execute (delete from ERP)
- Export results to CSV
- Hide/show checked items feature (User mode)
- Share Production IDs between ExtractorPage and CleanerPage
- Persist page state using sessionStorage

Bug Fixes:
- Fix MySQL query rowCount for INSERT/UPDATE/DELETE operations
- Add AUTO_INCREMENT to MaterialsToBeDeleted.ID (preserved 43 records)
- Add UNIQUE constraint on MaterialsToBeDeleted.MaterialCode

Files Added:
- src/main/ipc/validation-handler.ts
- src/main/types/validation.types.ts
- src/main/services/database/materials-to-be-deleted-dao.ts
- src/main/services/database/discrete-material-plan-dao.ts

Files Modified:
- src/renderer/src/pages/CleanerPage.tsx (complete rewrite)
- src/renderer/src/pages/ExtractorPage.tsx
- src/renderer/src/App.tsx (add navigation tabs)
- src/main/services/database/mysql.ts
- src/preload/index.ts + index.d.ts
This commit is contained in:
Misaka
2026-03-01 19:15:38 +08:00
parent fad08796f7
commit 0f1e0a8908
11 changed files with 2729 additions and 107 deletions

View File

@@ -8,6 +8,7 @@ import type {
UserSelectionResponse,
CurrentUserResponse
} from '../main/ipc/auth-handler'
import type { ValidationRequest, ValidationResponse } from '../main/types/validation.types'
/**
* Order number resolver API
@@ -69,6 +70,67 @@ export interface AuthAPI {
isAdmin: () => Promise<boolean>
}
/**
* Validation API
*/
export interface ValidationAPI {
/**
* Run material validation
* @param request - Validation request
*/
validate: (request: ValidationRequest) => Promise<ValidationResponse>
/**
* Set shared Production IDs from extractor page
* @param productionIds - List of Production IDs
*/
setSharedProductionIds: (productionIds: string[]) => Promise<void>
/**
* Get shared Production IDs
*/
getSharedProductionIds: () => Promise<{ productionIds: string[] }>
}
/**
* Materials API
*/
export interface MaterialsAPI {
/**
* Upsert batch materials to MaterialsToBeDeleted
* @param materials - List of materials with materialCode and managerName
*/
upsertBatch: (materials: { materialCode: string; managerName: string }[]) => Promise<{
success: boolean
stats?: { total: number; success: number; failed: number }
error?: string
}>
/**
* Delete materials by material codes
* @param materialCodes - List of material codes to delete
*/
delete: (materialCodes: string[]) => Promise<{
success: boolean
count?: number
error?: string
}>
/**
* Get unique manager names
*/
getManagers: () => Promise<{ managers: string[] }>
/**
* Get materials by manager
* @param managerName - Manager name
*/
getByManager: (managerName: string) => Promise<{ materials: unknown[] }>
/**
* Get all material records
*/
getAll: () => Promise<{ materials: unknown[] }>
/**
* Get statistics
*/
getStatistics: () => Promise<{ stats: unknown }>
}
declare global {
interface Window {
electron: {
@@ -93,6 +155,8 @@ declare global {
database: DatabaseAPI
resolver: ResolverAPI
auth: AuthAPI
validation: ValidationAPI
materials: MaterialsAPI
}
api: unknown
}

View File

@@ -6,6 +6,7 @@ import type { CleanerInput } from '../main/types/cleaner.types'
import type { ResolverInput } from '../main/ipc/resolver-handler'
import type { LoginRequest } from '../main/ipc/auth-handler'
import type { UserInfo } from '../main/types/user.types'
import type { ValidationRequest } from '../main/types/validation.types'
// Custom APIs for renderer
const api = {
@@ -61,6 +62,28 @@ const api = {
isSqlServerConnected: () => ipcRenderer.invoke('database:sqlserver:isConnected'),
querySqlServer: (sql: string, params?: Record<string, unknown>) =>
ipcRenderer.invoke('database:sqlserver:query', sql, params)
},
// Validation service
validation: {
validate: (request: ValidationRequest) => ipcRenderer.invoke('validation:validate', request),
setSharedProductionIds: (productionIds: string[]) =>
ipcRenderer.invoke('validation:setSharedProductionIds', productionIds),
getSharedProductionIds: () =>
ipcRenderer.invoke('validation:getSharedProductionIds')
},
// Materials service
materials: {
upsertBatch: (materials: { materialCode: string; managerName: string }[]) =>
ipcRenderer.invoke('materials:upsertBatch', { materials }),
delete: (materialCodes: string[]) =>
ipcRenderer.invoke('materials:delete', { materialCodes }),
getManagers: () => ipcRenderer.invoke('materials:getManagers'),
getByManager: (managerName: string) =>
ipcRenderer.invoke('materials:getByManager', managerName),
getAll: () => ipcRenderer.invoke('materials:getAll'),
getStatistics: () => ipcRenderer.invoke('materials:getStatistics')
}
} as const