Add category image upload with thumbnail backdrop in category tiles
- Add category image API methods (upload/delete) to src/api/index.js - Add uploadCategoryImage/deleteCategoryImage thunks and extraReducers in categorySlice - Add CategoryBackdrop component showing ThumbHash placeholder with full thumb overlay - Add photo upload/delete tab to CategoryEditTile settings cycle - Category tile label band uses semi-transparent black overlay with white text - Pass thumbHash/fullHash to both CategoryTile and CategoryEditTile
This commit is contained in:
@@ -238,6 +238,38 @@ export const toggleCategoryPos = createAsyncThunk(
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* POST /api/category/:id/image → 201 { category: { id, fullHash, thumbW, thumbH, thumbHash } }
|
||||
*/
|
||||
export const uploadCategoryImage = createAsyncThunk(
|
||||
'category/uploadImage',
|
||||
async ({ categoryId, file }, { rejectWithValue }) => {
|
||||
try {
|
||||
const data = await category.uploadImage(categoryId, file);
|
||||
requireObject(data.category, 'POST /api/category/:id/image');
|
||||
return data.category;
|
||||
} catch (err) {
|
||||
return catchBackendError(rejectWithValue)(err);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* DELETE /api/category/:id/image → 200 { category: { id, thumbW: null, thumbH: null, thumbHash: null } }
|
||||
*/
|
||||
export const deleteCategoryImage = createAsyncThunk(
|
||||
'category/deleteImage',
|
||||
async (categoryId, { rejectWithValue }) => {
|
||||
try {
|
||||
const data = await category.deleteImage(categoryId);
|
||||
requireObject(data.category, 'DELETE /api/category/:id/image');
|
||||
return data.category;
|
||||
} catch (err) {
|
||||
return catchBackendError(rejectWithValue)(err);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const initialState = {
|
||||
companyId: null,
|
||||
languageId: null,
|
||||
@@ -442,6 +474,30 @@ const categorySlice = createSlice({
|
||||
state.categories[key] = { ...existing, pos: updated.pos };
|
||||
}
|
||||
})
|
||||
.addCase(uploadCategoryImage.fulfilled, (state, action) => {
|
||||
const { id, fullHash, thumbW, thumbH, thumbHash } = action.payload;
|
||||
if (id == null || state.categories == null) return;
|
||||
const key = String(id);
|
||||
const existing = state.categories[key];
|
||||
if (existing) {
|
||||
state.categories[key] = { ...existing, fullHash, thumbW, thumbH, thumbHash };
|
||||
}
|
||||
})
|
||||
.addCase(deleteCategoryImage.fulfilled, (state, action) => {
|
||||
const { id } = action.payload;
|
||||
if (id == null || state.categories == null) return;
|
||||
const key = String(id);
|
||||
const existing = state.categories[key];
|
||||
if (existing) {
|
||||
state.categories[key] = {
|
||||
...existing,
|
||||
fullHash: null,
|
||||
thumbW: null,
|
||||
thumbH: null,
|
||||
thumbHash: null,
|
||||
};
|
||||
}
|
||||
})
|
||||
.addCase(loadCurrentCompany.fulfilled, (state, action) => {
|
||||
const nextCompanyId = action.payload?.id ?? null;
|
||||
if (nextCompanyId != null && nextCompanyId !== state.companyId) {
|
||||
|
||||
Reference in New Issue
Block a user