Добавил асинхронные функции и типыдля для дэшборда, slice, обновил store
This commit is contained in:
@@ -1,10 +1,18 @@
|
||||
import { configureStore } from "@reduxjs/toolkit";
|
||||
import authReducer from "../features/auth/authSlice";
|
||||
import dashboardReducer from '../features/dashboard/dashboardSlice'
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: {
|
||||
auth: authReducer,
|
||||
dashboard: dashboardReducer,
|
||||
},
|
||||
middleware: (getDefaultMiddleware) =>
|
||||
getDefaultMiddleware({
|
||||
serializableCheck: {
|
||||
ignoredActions: ['persist/PERSIST'],
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
export type RootState = ReturnType<typeof store.getState>;
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
import { createSlice} from '@reduxjs/toolkit';
|
||||
import type { DashboardState, Robot, ScanRecord, Statistics, AIPrediction } from '../../model/types/dashboardTypes';
|
||||
import type { PayloadAction } from '@reduxjs/toolkit';
|
||||
import { fetchDashboardData } from './dashboardThunks';
|
||||
import { fetchAIPredictions } from './dashboardThunks';
|
||||
|
||||
const initialState: DashboardState = {
|
||||
robots: [],
|
||||
recentScans: [],
|
||||
statistics: null,
|
||||
aiPredictions: [],
|
||||
isWebSocketConnected: false,
|
||||
isScanAutoUpdate: true,
|
||||
isLoading: false,
|
||||
};
|
||||
|
||||
const dashboardSlice = createSlice({
|
||||
name: 'dashboard',
|
||||
initialState,
|
||||
reducers: {
|
||||
setWebSocketConnection: (state, action: PayloadAction<boolean>) => {
|
||||
state.isWebSocketConnected = action.payload;
|
||||
},
|
||||
updateRobotData: (state, action: PayloadAction<Robot>) => {
|
||||
const index = state.robots.findIndex(r => r.id === action.payload.id);
|
||||
if (index >= 0) {
|
||||
state.robots[index] = action.payload;
|
||||
} else {
|
||||
state.robots.push(action.payload);
|
||||
}
|
||||
},
|
||||
addScanRecord: (state, action: PayloadAction<ScanRecord>) => {
|
||||
if (state.isScanAutoUpdate) {
|
||||
state.recentScans.unshift(action.payload);
|
||||
// Ограничиваем количество записей
|
||||
if (state.recentScans.length > 20) {
|
||||
state.recentScans = state.recentScans.slice(0, 20);
|
||||
}
|
||||
}
|
||||
},
|
||||
toggleScanAutoUpdate: (state) => {
|
||||
state.isScanAutoUpdate = !state.isScanAutoUpdate;
|
||||
},
|
||||
updateStatistics: (state, action: PayloadAction<Statistics>) => {
|
||||
state.statistics = action.payload;
|
||||
},
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder
|
||||
.addCase(fetchDashboardData.pending, (state) => {
|
||||
state.isLoading = true;
|
||||
})
|
||||
.addCase(fetchDashboardData.fulfilled, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.robots = action.payload.robots;
|
||||
state.recentScans = action.payload.recent_scans;
|
||||
state.statistics = action.payload.statistics;
|
||||
})
|
||||
.addCase(fetchDashboardData.rejected, (state) => {
|
||||
state.isLoading = false;
|
||||
})
|
||||
.addCase(fetchAIPredictions.fulfilled, (state, action) => {
|
||||
state.aiPredictions = action.payload.predictions;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const {
|
||||
setWebSocketConnection,
|
||||
updateRobotData,
|
||||
addScanRecord,
|
||||
toggleScanAutoUpdate,
|
||||
updateStatistics,
|
||||
} = dashboardSlice.actions;
|
||||
|
||||
export default dashboardSlice.reducer;
|
||||
@@ -0,0 +1,51 @@
|
||||
import { createAsyncThunk } from '@reduxjs/toolkit';
|
||||
|
||||
// Получение текущего состояния дашборда
|
||||
export const fetchDashboardData = createAsyncThunk(
|
||||
'dashboard/fetchData',
|
||||
async (_, { rejectWithValue }) => {
|
||||
try {
|
||||
const response = await fetch('/api/dashboard/current', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${localStorage.getItem('token')}`,
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Ошибка загрузки данных');
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
} catch (error: any) {
|
||||
return rejectWithValue(error.message);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Получение прогнозов от ИИ
|
||||
export const fetchAIPredictions = createAsyncThunk(
|
||||
'dashboard/fetchPredictions',
|
||||
async (_, { rejectWithValue }) => {
|
||||
try {
|
||||
const response = await fetch('/api/ai/predict', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${localStorage.getItem('token')}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
period_days: 7,
|
||||
categories: [],
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Ошибка получения прогнозов');
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
} catch (error: any) {
|
||||
return rejectWithValue(error.message);
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,46 @@
|
||||
export interface Robot {
|
||||
id: string;
|
||||
name: string;
|
||||
batteryLevel: number;
|
||||
status: 'active' | 'low_battery' | 'offline';
|
||||
location: string;
|
||||
lastUpdate: string;
|
||||
}
|
||||
|
||||
export interface ScanRecord {
|
||||
id: string;
|
||||
timestamp: string;
|
||||
robotId: string;
|
||||
zone: string;
|
||||
productName: string;
|
||||
productSku: string;
|
||||
quantity: number;
|
||||
status: 'ok' | 'low_stock' | 'critical';
|
||||
}
|
||||
|
||||
export interface Statistics {
|
||||
activeRobots: number;
|
||||
totalRobots: number;
|
||||
scannedToday: number;
|
||||
criticalItems: number;
|
||||
averageBattery: number;
|
||||
}
|
||||
|
||||
export interface AIPrediction {
|
||||
id: string;
|
||||
productName: string;
|
||||
currentStock: number;
|
||||
predictedDepletionDate: string;
|
||||
recommendedOrder: number;
|
||||
confidence: number;
|
||||
}
|
||||
|
||||
export interface DashboardState {
|
||||
robots: Robot[];
|
||||
recentScans: ScanRecord[];
|
||||
statistics: Statistics | null;
|
||||
aiPredictions: AIPrediction[];
|
||||
isWebSocketConnected: boolean;
|
||||
isScanAutoUpdate: boolean;
|
||||
isLoading: boolean;
|
||||
}
|
||||
Reference in New Issue
Block a user