update and create article provider functions
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user