update and create article provider functions

This commit is contained in:
2025-11-15 05:44:18 +05:30
parent 1e6c80f1b3
commit ae0bc7dd12
3 changed files with 80 additions and 4 deletions

View File

@@ -1,12 +1,14 @@
import * as React from 'react'; import * as React from 'react';
import { Box, Typography, Divider, IconButton, Chip, TextField, Button } from '@mui/material'; import { Box, Typography, Divider, IconButton, TextField, Button } from '@mui/material';
import { styled } from '@mui/material/styles'; import { styled } from '@mui/material/styles';
import ArrowBackRoundedIcon from '@mui/icons-material/ArrowBackRounded'; import ArrowBackRoundedIcon from '@mui/icons-material/ArrowBackRounded';
import { ArticleEditorProps } from '../../types/props'; import { ArticleEditorProps } from '../../types/props';
import { ArticleModel } from "../../types/models";
import { useUpload } from "../../providers/Upload";
import { useArticles } from "../../providers/Article";
import ReactMarkdown from 'react-markdown'; import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm'; import remarkGfm from 'remark-gfm';
import ImageUploadField from "../ImageUploadField"; import ImageUploadField from "../ImageUploadField";
import { useUpload } from "../../providers/Upload";
const ArticleContainer = styled(Box)(({ theme }) => ({ const ArticleContainer = styled(Box)(({ theme }) => ({
maxWidth: '800px', maxWidth: '800px',
@@ -31,6 +33,7 @@ export default function ArticleView({
}: ArticleEditorProps) { }: ArticleEditorProps) {
const { uploadFile } = useUpload(); const { uploadFile } = useUpload();
const { updateArticle, createArticle } = useArticles();
const [title, setTitle] = React.useState(article?.title ?? ""); const [title, setTitle] = React.useState(article?.title ?? "");
const [description, setDescription] = React.useState(article?.description ?? ""); const [description, setDescription] = React.useState(article?.description ?? "");
@@ -54,6 +57,18 @@ export default function ArticleView({
} }
}; };
const handleSaveArticle = async (articleData: Partial<ArticleModel>) => {
// If _id exists → UPDATE
if (articleData._id) {
console.log("Updating article with ID:", articleData._id);
return await updateArticle(articleData as ArticleModel);
}
// No _id → CREATE
console.log("Creating new article:", articleData);
return await createArticle(articleData as ArticleModel);
};
return ( return (
<ArticleContainer> <ArticleContainer>
{/* BACK BUTTON */} {/* BACK BUTTON */}
@@ -159,7 +174,7 @@ export default function ArticleView({
variant="contained" variant="contained"
color="primary" color="primary"
onClick={() => onClick={() =>
console.log({ handleSaveArticle({
...article, ...article,
title, title,
tag, tag,

View File

@@ -12,6 +12,14 @@ export const ArticleProvider: React.FC<{ children: React.ReactNode }> = ({ child
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const { token } = useAuth(); const { token } = useAuth();
/** 🔹 Author IDs must be strings for API, so we normalize here */
const normalizeArticleForApi = (article: Partial<ArticleModel>) => ({
...article,
authors: (article.authors ?? []).map(a =>
a._id
),
});
/** 🔹 Fetch articles (JWT automatically attached by api.ts interceptor) */ /** 🔹 Fetch articles (JWT automatically attached by api.ts interceptor) */
const fetchArticles = async () => { const fetchArticles = async () => {
try { try {
@@ -29,6 +37,50 @@ export const ArticleProvider: React.FC<{ children: React.ReactNode }> = ({ child
} }
}; };
/** 🔹 Update article */
const updateArticle = async (articleData: ArticleModel) => {
if (!articleData._id) {
console.error('updateArticle called without _id');
return;
}
const normalizedArticleData = normalizeArticleForApi(articleData);
try {
setLoading(true);
setError(null);
const res = await api.put<ArticleModel>(`/articles/${articleData._id}`, normalizedArticleData);
return res.data;
} catch (err: any) {
console.error('Article update failed:', err);
setError(err.response?.data?.detail || 'Failed to update article');
} finally {
setLoading(false);
}
};
/** 🔹 Create article */
const createArticle = async (articleData: ArticleModel) => {
if (articleData._id) {
console.error('createArticle called with _id');
return;
}
const normalizedArticleData = normalizeArticleForApi(articleData);
try {
setLoading(true);
setError(null);
const res = await api.post<ArticleModel>(`/articles`, normalizedArticleData);
return res.data;
} catch (err: any) {
console.error('Article create failed:', err);
setError(err.response?.data?.detail || 'Failed to create article');
} finally {
setLoading(false);
}
};
/** 🔹 Auto-fetch articles whenever user logs in/out */ /** 🔹 Auto-fetch articles whenever user logs in/out */
useEffect(() => { useEffect(() => {
// Always load once on mount // Always load once on mount
@@ -41,7 +93,14 @@ export const ArticleProvider: React.FC<{ children: React.ReactNode }> = ({ child
}, [token]); }, [token]);
return ( return (
<ArticleContext.Provider value={{ articles, loading, error, refreshArticles: fetchArticles }}> <ArticleContext.Provider value={{
articles,
loading,
error,
refreshArticles: fetchArticles,
updateArticle,
createArticle,
}}>
{children} {children}
</ArticleContext.Provider> </ArticleContext.Provider>
); );

View File

@@ -5,6 +5,8 @@ export interface ArticleContextModel {
loading: boolean; loading: boolean;
error: string | null; error: string | null;
refreshArticles: () => Promise<void>; refreshArticles: () => Promise<void>;
updateArticle: (user: ArticleModel) => Promise<ArticleModel | void>;
createArticle: (user: ArticleModel) => Promise<ArticleModel | void>;
} }
export interface AuthContextModel { export interface AuthContextModel {