Добавил асинхронные функции и типыдля для дэшборда, slice, обновил store

This commit is contained in:
2025-10-31 00:50:19 +03:00
parent c18ba8e0c7
commit 52f286a8de
4 changed files with 181 additions and 0 deletions
+8
View File
@@ -1,10 +1,18 @@
import { configureStore } from "@reduxjs/toolkit"; import { configureStore } from "@reduxjs/toolkit";
import authReducer from "../features/auth/authSlice"; import authReducer from "../features/auth/authSlice";
import dashboardReducer from '../features/dashboard/dashboardSlice'
export const store = configureStore({ export const store = configureStore({
reducer: { reducer: {
auth: authReducer, auth: authReducer,
dashboard: dashboardReducer,
}, },
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: {
ignoredActions: ['persist/PERSIST'],
},
}),
}); });
export type RootState = ReturnType<typeof store.getState>; 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);
}
}
);
+46
View File
@@ -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;
}