diff --git a/src/components/article/ArticleView.jsx b/src/components/article/ArticleView.jsx index 60fb65e..66455f5 100644 --- a/src/components/article/ArticleView.jsx +++ b/src/components/article/ArticleView.jsx @@ -12,6 +12,8 @@ import { patchArticleInLists, removeArticleBySku, selectCategoryRootId, + loadCategoryArticles, + selectCurrentCategoryId, } from '../../store/categorySlice'; import ArticleTileTab from './ArticleTileTab'; @@ -33,6 +35,7 @@ export default function ArticleView({ const dispatch = useDispatch(); const categories = useSelector((state) => state.category.categories); const rootCategoryId = useSelector(selectCategoryRootId); + const currentCategoryId = useSelector(selectCurrentCategoryId); const syncCategoryListPhoto = (next) => { const photo = primaryPhoto(next); @@ -57,6 +60,12 @@ export default function ArticleView({ categoryIds: next.categoryIds, }), ); + + // Reload articles to reflect the update + // The reducer handles removing from lists where article no longer belongs + if (currentCategoryId != null && String(currentCategoryId) !== String(rootCategoryId)) { + dispatch(loadCategoryArticles(currentCategoryId)); + } }; const handleArticleDeleted = () => { diff --git a/src/store/categorySlice.js b/src/store/categorySlice.js index f713ff8..12dbca5 100644 --- a/src/store/categorySlice.js +++ b/src/store/categorySlice.js @@ -343,14 +343,55 @@ const categorySlice = createSlice({ state.articlesTotal = state.articles.length; state.rootArticles = state.rootArticles.filter((row) => row.sku !== sku); }, - /** Keep category tile list in sync with article detail (e.g. after photo upload). */ + /** Keep category tile list in sync with article detail (e.g. after photo upload or category change). */ patchArticleInLists(state, action) { - const { sku, ...fields } = action.payload; + const { sku, categoryIds, ...fields } = action.payload; if (sku == null) return; + + // Merge fields into existing article in both lists const patchList = (list) => - list.map((row) => (row.sku === sku ? { ...row, ...fields } : row)); + list.map((row) => { + if (row.sku !== sku) return row; + return { ...row, ...fields }; + }); state.articles = patchList(state.articles); state.rootArticles = patchList(state.rootArticles); + + if (categoryIds === undefined) return; + const newCategoryIds = categoryIds ?? []; + + // Helper: find the article object in either list + const findArticle = (list) => list.find((row) => row.sku === sku); + const articleObj = findArticle(state.articles) || findArticle(state.rootArticles); + if (!articleObj) return; + + // Handle root category membership + if (state.rootCategoryId != null) { + const inRootCategory = newCategoryIds.some( + (id) => String(id) === String(state.rootCategoryId), + ); + const alreadyInRoot = state.rootArticles.some((row) => row.sku === sku); + if (!inRootCategory) { + state.rootArticles = state.rootArticles.filter((row) => row.sku !== sku); + } else if (!alreadyInRoot) { + state.rootArticles = [...state.rootArticles, articleObj]; + } + } + + // Handle current category membership + if (state.currentCategoryId != null) { + const inCurrentCategory = newCategoryIds.some( + (id) => String(id) === String(state.currentCategoryId), + ); + const alreadyInCurrent = state.articles.some((row) => row.sku === sku); + if (!inCurrentCategory) { + state.articles = state.articles.filter((row) => row.sku !== sku); + state.articlesTotal = state.articles.length; + } else if (!alreadyInCurrent) { + state.articles = [...state.articles, articleObj]; + state.articlesTotal = state.articles.length; + } + } }, }, extraReducers: (builder) => {