225 lines
6.7 KiB
TypeScript
225 lines
6.7 KiB
TypeScript
import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit';
|
|
import axios from '../../axios';
|
|
|
|
// Типы данных
|
|
export interface Submit {
|
|
id?: number;
|
|
missionId: number;
|
|
language: string;
|
|
languageVersion: string;
|
|
sourceCode: string;
|
|
contestId?: number;
|
|
}
|
|
|
|
export interface Solution {
|
|
id: number;
|
|
missionId: number;
|
|
language: string;
|
|
languageVersion: string;
|
|
sourceCode: string;
|
|
status: string;
|
|
time: string;
|
|
testerState: string;
|
|
testerErrorCode: string;
|
|
testerMessage: string;
|
|
currentTest: number;
|
|
amountOfTests: number;
|
|
}
|
|
|
|
export interface MissionSubmit {
|
|
id: number;
|
|
userId: number;
|
|
solution: Solution;
|
|
contestId?: number;
|
|
contestName?: string;
|
|
sourceType: string;
|
|
}
|
|
|
|
interface SubmitState {
|
|
submits: Submit[];
|
|
submitsById: Record<number, MissionSubmit[]>; // ✅ добавлено
|
|
currentSubmit?: Submit;
|
|
status: 'idle' | 'loading' | 'successful' | 'failed';
|
|
error?: string;
|
|
}
|
|
|
|
// Начальное состояние
|
|
const initialState: SubmitState = {
|
|
submits: [],
|
|
submitsById: {}, // ✅ инициализация
|
|
currentSubmit: undefined,
|
|
status: 'idle',
|
|
error: undefined,
|
|
};
|
|
|
|
// AsyncThunk: Отправка решения
|
|
export const submitMission = createAsyncThunk(
|
|
'submit/submitMission',
|
|
async (submitData: Submit, { rejectWithValue }) => {
|
|
try {
|
|
const response = await axios.post('/submits', submitData);
|
|
return response.data;
|
|
} catch (err: any) {
|
|
return rejectWithValue(
|
|
err.response?.data?.message || 'Submit failed',
|
|
);
|
|
}
|
|
},
|
|
);
|
|
|
|
// AsyncThunk: Получить все свои отправки
|
|
export const fetchMySubmits = createAsyncThunk(
|
|
'submit/fetchMySubmits',
|
|
async (_, { rejectWithValue }) => {
|
|
try {
|
|
const response = await axios.get('/submits/my');
|
|
return response.data as Submit[];
|
|
} catch (err: any) {
|
|
return rejectWithValue(
|
|
err.response?.data?.message || 'Failed to fetch submits',
|
|
);
|
|
}
|
|
},
|
|
);
|
|
|
|
// AsyncThunk: Получить конкретную отправку по ID
|
|
export const fetchSubmitById = createAsyncThunk(
|
|
'submit/fetchSubmitById',
|
|
async (id: number, { rejectWithValue }) => {
|
|
try {
|
|
const response = await axios.get(`/submits/${id}`);
|
|
return response.data as Submit;
|
|
} catch (err: any) {
|
|
return rejectWithValue(
|
|
err.response?.data?.message || 'Failed to fetch submit',
|
|
);
|
|
}
|
|
},
|
|
);
|
|
|
|
// ✅ AsyncThunk: Получить отправки для конкретной миссии (новая структура)
|
|
export const fetchMySubmitsByMission = createAsyncThunk(
|
|
'submit/fetchMySubmitsByMission',
|
|
async (missionId: number, { rejectWithValue }) => {
|
|
try {
|
|
const response = await axios.get(
|
|
`/submits/my/mission/${missionId}`,
|
|
);
|
|
return { missionId, data: response.data as MissionSubmit[] };
|
|
} catch (err: any) {
|
|
return rejectWithValue(
|
|
err.response?.data?.message ||
|
|
'Failed to fetch mission submits',
|
|
);
|
|
}
|
|
},
|
|
);
|
|
|
|
// Slice
|
|
const submitSlice = createSlice({
|
|
name: 'submit',
|
|
initialState,
|
|
reducers: {
|
|
clearCurrentSubmit: (state) => {
|
|
state.currentSubmit = undefined;
|
|
state.status = 'idle';
|
|
state.error = undefined;
|
|
},
|
|
clearSubmitsByMission: (state, action: PayloadAction<number>) => {
|
|
delete state.submitsById[action.payload];
|
|
},
|
|
},
|
|
extraReducers: (builder) => {
|
|
// Отправка решения
|
|
builder.addCase(submitMission.pending, (state) => {
|
|
state.status = 'loading';
|
|
state.error = undefined;
|
|
});
|
|
builder.addCase(
|
|
submitMission.fulfilled,
|
|
(state, action: PayloadAction<Submit>) => {
|
|
state.status = 'successful';
|
|
state.submits.push(action.payload);
|
|
},
|
|
);
|
|
builder.addCase(
|
|
submitMission.rejected,
|
|
(state, action: PayloadAction<any>) => {
|
|
state.status = 'failed';
|
|
state.error = action.payload;
|
|
},
|
|
);
|
|
|
|
// Получить все свои отправки
|
|
builder.addCase(fetchMySubmits.pending, (state) => {
|
|
state.status = 'loading';
|
|
state.error = undefined;
|
|
});
|
|
builder.addCase(
|
|
fetchMySubmits.fulfilled,
|
|
(state, action: PayloadAction<Submit[]>) => {
|
|
state.status = 'successful';
|
|
state.submits = action.payload;
|
|
},
|
|
);
|
|
builder.addCase(
|
|
fetchMySubmits.rejected,
|
|
(state, action: PayloadAction<any>) => {
|
|
state.status = 'failed';
|
|
state.error = action.payload;
|
|
},
|
|
);
|
|
|
|
// Получить отправку по ID
|
|
builder.addCase(fetchSubmitById.pending, (state) => {
|
|
state.status = 'loading';
|
|
state.error = undefined;
|
|
});
|
|
builder.addCase(
|
|
fetchSubmitById.fulfilled,
|
|
(state, action: PayloadAction<Submit>) => {
|
|
state.status = 'successful';
|
|
state.currentSubmit = action.payload;
|
|
},
|
|
);
|
|
builder.addCase(
|
|
fetchSubmitById.rejected,
|
|
(state, action: PayloadAction<any>) => {
|
|
state.status = 'failed';
|
|
state.error = action.payload;
|
|
},
|
|
);
|
|
|
|
// ✅ Получить отправки по миссии
|
|
builder.addCase(fetchMySubmitsByMission.pending, (state) => {
|
|
state.status = 'loading';
|
|
state.error = undefined;
|
|
});
|
|
builder.addCase(
|
|
fetchMySubmitsByMission.fulfilled,
|
|
(
|
|
state,
|
|
action: PayloadAction<{
|
|
missionId: number;
|
|
data: MissionSubmit[];
|
|
}>,
|
|
) => {
|
|
state.status = 'successful';
|
|
state.submitsById[action.payload.missionId] =
|
|
action.payload.data;
|
|
},
|
|
);
|
|
builder.addCase(
|
|
fetchMySubmitsByMission.rejected,
|
|
(state, action: PayloadAction<any>) => {
|
|
state.status = 'failed';
|
|
state.error = action.payload;
|
|
},
|
|
);
|
|
},
|
|
});
|
|
|
|
export const { clearCurrentSubmit, clearSubmitsByMission } =
|
|
submitSlice.actions;
|
|
export const submitReducer = submitSlice.reducer;
|