upload mission
This commit is contained in:
@@ -6,7 +6,7 @@ interface AuthState {
|
||||
jwt: string | null;
|
||||
refreshToken: string | null;
|
||||
username: string | null;
|
||||
status: "idle" | "loading" | "succeeded" | "failed";
|
||||
status: "idle" | "loading" | "successful" | "failed";
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ const authSlice = createSlice({
|
||||
state.error = null;
|
||||
});
|
||||
builder.addCase(registerUser.fulfilled, (state, action: PayloadAction<{ jwt: string; refreshToken: string }>) => {
|
||||
state.status = "succeeded";
|
||||
state.status = "successful";
|
||||
axios.defaults.headers.common['Authorization'] = `Bearer ${action.payload.jwt}`;
|
||||
state.jwt = action.payload.jwt;
|
||||
state.refreshToken = action.payload.refreshToken;
|
||||
@@ -113,7 +113,7 @@ const authSlice = createSlice({
|
||||
state.error = null;
|
||||
});
|
||||
builder.addCase(loginUser.fulfilled, (state, action: PayloadAction<{ jwt: string; refreshToken: string }>) => {
|
||||
state.status = "succeeded";
|
||||
state.status = "successful";
|
||||
axios.defaults.headers.common['Authorization'] = `Bearer ${action.payload.jwt}`;
|
||||
state.jwt = action.payload.jwt;
|
||||
state.refreshToken = action.payload.refreshToken;
|
||||
@@ -129,7 +129,7 @@ const authSlice = createSlice({
|
||||
state.error = null;
|
||||
});
|
||||
builder.addCase(refreshToken.fulfilled, (state, action: PayloadAction<{ username: string }>) => {
|
||||
state.status = "succeeded";
|
||||
state.status = "successful";
|
||||
state.username = action.payload.username;
|
||||
});
|
||||
builder.addCase(refreshToken.rejected, (state, action: PayloadAction<any>) => {
|
||||
@@ -143,7 +143,7 @@ const authSlice = createSlice({
|
||||
state.error = null;
|
||||
});
|
||||
builder.addCase(fetchWhoAmI.fulfilled, (state, action: PayloadAction<{ username: string }>) => {
|
||||
state.status = "succeeded";
|
||||
state.status = "successful";
|
||||
state.username = action.payload.username;
|
||||
});
|
||||
builder.addCase(fetchWhoAmI.rejected, (state, action: PayloadAction<any>) => {
|
||||
|
||||
146
src/redux/slices/missions.ts
Normal file
146
src/redux/slices/missions.ts
Normal file
@@ -0,0 +1,146 @@
|
||||
import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit";
|
||||
import axios from "../../axios";
|
||||
|
||||
// Типы данных
|
||||
interface Statement {
|
||||
id: number;
|
||||
language: string;
|
||||
statementTexts: Record<string, string>;
|
||||
mediaFiles?: { id: number; fileName: string; mediaUrl: string }[];
|
||||
}
|
||||
|
||||
interface Mission {
|
||||
id: number;
|
||||
authorId: number;
|
||||
name: string;
|
||||
difficulty: number;
|
||||
tags: string[];
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
statements: Statement[] | null;
|
||||
}
|
||||
|
||||
interface MissionsState {
|
||||
missions: Mission[];
|
||||
currentMission: Mission | null;
|
||||
hasNextPage: boolean;
|
||||
status: "idle" | "loading" | "successful" | "failed";
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
// Инициализация состояния
|
||||
const initialState: MissionsState = {
|
||||
missions: [],
|
||||
currentMission: null,
|
||||
hasNextPage: false,
|
||||
status: "idle",
|
||||
error: null,
|
||||
};
|
||||
|
||||
// AsyncThunk: Получение списка миссий
|
||||
export const fetchMissions = createAsyncThunk(
|
||||
"missions/fetchMissions",
|
||||
async (
|
||||
{ page = 0, pageSize = 10, tags }: { page?: number; pageSize?: number; tags?: string[] },
|
||||
{ rejectWithValue }
|
||||
) => {
|
||||
try {
|
||||
const params: any = { page, pageSize };
|
||||
if (tags) params.tags = tags;
|
||||
const response = await axios.get("/missions", { params });
|
||||
return response.data; // { hasNextPage, missions }
|
||||
} catch (err: any) {
|
||||
return rejectWithValue(err.response?.data?.message || "Failed to fetch missions");
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// AsyncThunk: Получение миссии по id
|
||||
export const fetchMissionById = createAsyncThunk(
|
||||
"missions/fetchMissionById",
|
||||
async (id: number, { rejectWithValue }) => {
|
||||
try {
|
||||
const response = await axios.get(`/missions/${id}`);
|
||||
return response.data; // Mission
|
||||
} catch (err: any) {
|
||||
return rejectWithValue(err.response?.data?.message || "Failed to fetch mission");
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// AsyncThunk: Загрузка миссии
|
||||
export const uploadMission = createAsyncThunk(
|
||||
"missions/uploadMission",
|
||||
async (
|
||||
{ file, name, difficulty, tags }: { file: File; name: string; difficulty: number; tags: string[] },
|
||||
{ rejectWithValue }
|
||||
) => {
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append("MissionFile", file);
|
||||
formData.append("Name", name);
|
||||
formData.append("Difficulty", difficulty.toString());
|
||||
tags.forEach(tag => formData.append("Tags", tag));
|
||||
|
||||
const response = await axios.post("/missions/upload", formData, {
|
||||
headers: { "Content-Type": "multipart/form-data" },
|
||||
});
|
||||
return response.data; // Mission
|
||||
} catch (err: any) {
|
||||
return rejectWithValue(err.response?.data?.message || "Failed to upload mission");
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Slice
|
||||
const missionsSlice = createSlice({
|
||||
name: "missions",
|
||||
initialState,
|
||||
reducers: {},
|
||||
extraReducers: (builder) => {
|
||||
// fetchMissions
|
||||
builder.addCase(fetchMissions.pending, (state) => {
|
||||
state.status = "loading";
|
||||
state.error = null;
|
||||
});
|
||||
builder.addCase(fetchMissions.fulfilled, (state, action: PayloadAction<{ missions: Mission[]; hasNextPage: boolean }>) => {
|
||||
state.status = "successful";
|
||||
state.missions = action.payload.missions;
|
||||
state.hasNextPage = action.payload.hasNextPage;
|
||||
});
|
||||
builder.addCase(fetchMissions.rejected, (state, action: PayloadAction<any>) => {
|
||||
state.status = "failed";
|
||||
state.error = action.payload;
|
||||
});
|
||||
|
||||
// fetchMissionById
|
||||
builder.addCase(fetchMissionById.pending, (state) => {
|
||||
state.status = "loading";
|
||||
state.error = null;
|
||||
});
|
||||
builder.addCase(fetchMissionById.fulfilled, (state, action: PayloadAction<Mission>) => {
|
||||
state.status = "successful";
|
||||
state.currentMission = action.payload;
|
||||
});
|
||||
builder.addCase(fetchMissionById.rejected, (state, action: PayloadAction<any>) => {
|
||||
state.status = "failed";
|
||||
state.error = action.payload;
|
||||
});
|
||||
|
||||
// uploadMission
|
||||
builder.addCase(uploadMission.pending, (state) => {
|
||||
state.status = "loading";
|
||||
state.error = null;
|
||||
});
|
||||
builder.addCase(uploadMission.fulfilled, (state, action: PayloadAction<Mission>) => {
|
||||
state.status = "successful";
|
||||
state.missions.unshift(action.payload); // Добавляем новую миссию в начало списка
|
||||
});
|
||||
builder.addCase(uploadMission.rejected, (state, action: PayloadAction<any>) => {
|
||||
state.status = "failed";
|
||||
state.error = action.payload;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const missionsReducer = missionsSlice.reducer;
|
||||
@@ -1,6 +1,7 @@
|
||||
import { configureStore } from "@reduxjs/toolkit";
|
||||
import { authReducer } from "./slices/auth";
|
||||
import { storeReducer } from "./slices/store";
|
||||
import { missionsReducer } from "./slices/missions";
|
||||
|
||||
|
||||
// использование
|
||||
@@ -17,6 +18,7 @@ export const store = configureStore({
|
||||
//user: userReducer,
|
||||
auth: authReducer,
|
||||
store: storeReducer,
|
||||
missions: missionsReducer,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user