Files
LiquidCode_Frontend/src/redux/slices/articles.ts
Виталий Лавшонок ded41ba7f0 update articles slice
2025-11-13 16:32:32 +03:00

360 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit';
import axios from '../../axios';
// =====================
// Типы
// =====================
type Status = 'idle' | 'loading' | 'successful' | 'failed';
export interface Article {
id: number;
authorId: number;
name: string;
content: string;
tags: string[];
createdAt: string;
updatedAt: string;
}
interface ArticlesResponse {
hasNextPage: boolean;
articles: Article[];
}
// =====================
// Состояние
// =====================
interface ArticlesState {
fetchArticles: {
articles: Article[];
hasNextPage: boolean;
status: Status;
error?: string;
};
fetchArticleById: {
article?: Article;
status: Status;
error?: string;
};
createArticle: {
article?: Article;
status: Status;
error?: string;
};
updateArticle: {
article?: Article;
status: Status;
error?: string;
};
deleteArticle: {
status: Status;
error?: string;
};
fetchMyArticles: {
articles: Article[];
status: Status;
error?: string;
};
}
const initialState: ArticlesState = {
fetchArticles: {
articles: [],
hasNextPage: false,
status: 'idle',
error: undefined,
},
fetchArticleById: {
article: undefined,
status: 'idle',
error: undefined,
},
createArticle: {
article: undefined,
status: 'idle',
error: undefined,
},
updateArticle: {
article: undefined,
status: 'idle',
error: undefined,
},
deleteArticle: {
status: 'idle',
error: undefined,
},
fetchMyArticles: {
articles: [],
status: 'idle',
error: undefined,
},
};
// =====================
// Async Thunks
// =====================
// Все статьи
export const fetchArticles = createAsyncThunk(
'articles/fetchArticles',
async (
{
page = 0,
pageSize = 10,
tags,
}: { page?: number; pageSize?: number; tags?: string[] } = {},
{ rejectWithValue },
) => {
try {
const params: any = { page, pageSize };
if (tags && tags.length > 0) params.tags = tags;
const response = await axios.get<ArticlesResponse>('/articles', {
params,
});
return response.data;
} catch (err: any) {
return rejectWithValue(
err.response?.data?.message || 'Ошибка при получении статей',
);
}
},
);
// Мои статьи
export const fetchMyArticles = createAsyncThunk(
'articles/fetchMyArticles',
async (_, { rejectWithValue }) => {
try {
const response = await axios.get<Article[]>('/articles/my');
return response.data;
} catch (err: any) {
return rejectWithValue(
err.response?.data?.message ||
'Ошибка при получении моих статей',
);
}
},
);
// Статья по ID
export const fetchArticleById = createAsyncThunk(
'articles/fetchById',
async (articleId: number, { rejectWithValue }) => {
try {
const response = await axios.get<Article>(`/articles/${articleId}`);
return response.data;
} catch (err: any) {
return rejectWithValue(
err.response?.data?.message || 'Ошибка при получении статьи',
);
}
},
);
// Создание статьи
export const createArticle = createAsyncThunk(
'articles/create',
async (
{
name,
content,
tags,
}: { name: string; content: string; tags: string[] },
{ rejectWithValue },
) => {
try {
const response = await axios.post<Article>('/articles', {
name,
content,
tags,
});
return response.data;
} catch (err: any) {
return rejectWithValue(
err.response?.data?.message || 'Ошибка при создании статьи',
);
}
},
);
// Обновление статьи
export const updateArticle = createAsyncThunk(
'articles/update',
async (
{
articleId,
name,
content,
tags,
}: { articleId: number; name: string; content: string; tags: string[] },
{ rejectWithValue },
) => {
try {
const response = await axios.put<Article>(
`/articles/${articleId}`,
{
name,
content,
tags,
},
);
return response.data;
} catch (err: any) {
return rejectWithValue(
err.response?.data?.message || 'Ошибка при обновлении статьи',
);
}
},
);
// Удаление статьи
export const deleteArticle = createAsyncThunk(
'articles/delete',
async (articleId: number, { rejectWithValue }) => {
try {
await axios.delete(`/articles/${articleId}`);
return articleId;
} catch (err: any) {
return rejectWithValue(
err.response?.data?.message || 'Ошибка при удалении статьи',
);
}
},
);
// =====================
// Slice
// =====================
const articlesSlice = createSlice({
name: 'articles',
initialState,
reducers: {
setArticlesStatus: (
state,
action: PayloadAction<{ key: keyof ArticlesState; status: Status }>,
) => {
const { key, status } = action.payload;
if (state[key]) {
(state[key] as any).status = status;
}
},
},
extraReducers: (builder) => {
// fetchArticles
builder.addCase(fetchArticles.pending, (state) => {
state.fetchArticles.status = 'loading';
state.fetchArticles.error = undefined;
});
builder.addCase(
fetchArticles.fulfilled,
(state, action: PayloadAction<ArticlesResponse>) => {
state.fetchArticles.status = 'successful';
state.fetchArticles.articles = action.payload.articles;
state.fetchArticles.hasNextPage = action.payload.hasNextPage;
},
);
builder.addCase(fetchArticles.rejected, (state, action: any) => {
state.fetchArticles.status = 'failed';
state.fetchArticles.error = action.payload;
});
// fetchMyArticles
builder.addCase(fetchMyArticles.pending, (state) => {
state.fetchMyArticles.status = 'loading';
state.fetchMyArticles.error = undefined;
});
builder.addCase(
fetchMyArticles.fulfilled,
(state, action: PayloadAction<Article[]>) => {
state.fetchMyArticles.status = 'successful';
state.fetchMyArticles.articles = action.payload;
},
);
builder.addCase(fetchMyArticles.rejected, (state, action: any) => {
state.fetchMyArticles.status = 'failed';
state.fetchMyArticles.error = action.payload;
});
// fetchArticleById
builder.addCase(fetchArticleById.pending, (state) => {
state.fetchArticleById.status = 'loading';
state.fetchArticleById.error = undefined;
});
builder.addCase(
fetchArticleById.fulfilled,
(state, action: PayloadAction<Article>) => {
state.fetchArticleById.status = 'successful';
state.fetchArticleById.article = action.payload;
},
);
builder.addCase(fetchArticleById.rejected, (state, action: any) => {
state.fetchArticleById.status = 'failed';
state.fetchArticleById.error = action.payload;
});
// createArticle
builder.addCase(createArticle.pending, (state) => {
state.createArticle.status = 'loading';
state.createArticle.error = undefined;
});
builder.addCase(
createArticle.fulfilled,
(state, action: PayloadAction<Article>) => {
state.createArticle.status = 'successful';
state.createArticle.article = action.payload;
},
);
builder.addCase(createArticle.rejected, (state, action: any) => {
state.createArticle.status = 'failed';
state.createArticle.error = action.payload;
});
// updateArticle
builder.addCase(updateArticle.pending, (state) => {
state.updateArticle.status = 'loading';
state.updateArticle.error = undefined;
});
builder.addCase(
updateArticle.fulfilled,
(state, action: PayloadAction<Article>) => {
state.updateArticle.status = 'successful';
state.updateArticle.article = action.payload;
},
);
builder.addCase(updateArticle.rejected, (state, action: any) => {
state.updateArticle.status = 'failed';
state.updateArticle.error = action.payload;
});
// deleteArticle
builder.addCase(deleteArticle.pending, (state) => {
state.deleteArticle.status = 'loading';
state.deleteArticle.error = undefined;
});
builder.addCase(
deleteArticle.fulfilled,
(state, action: PayloadAction<number>) => {
state.deleteArticle.status = 'successful';
state.fetchArticles.articles =
state.fetchArticles.articles.filter(
(a) => a.id !== action.payload,
);
state.fetchMyArticles.articles =
state.fetchMyArticles.articles.filter(
(a) => a.id !== action.payload,
);
},
);
builder.addCase(deleteArticle.rejected, (state, action: any) => {
state.deleteArticle.status = 'failed';
state.deleteArticle.error = action.payload;
});
},
});
export const { setArticlesStatus } = articlesSlice.actions;
export const articlesReducer = articlesSlice.reducer;