mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-30 02:23:47 +00:00
fix: tag color bug, FormData interceptor, falsy id check
- Fix tag using category_color instead of its own color when editing - Add FormData auto-detection interceptor in client.ts (root cause of 422) - Remove redundant Content-Type overrides from all 5 upload endpoints - Fix falsy id check (id: 0 treated as null) with strict comparison - Await invalidateQueries to prevent stale dropdown data
This commit is contained in:
@@ -242,11 +242,7 @@ export const adminBroadcastsApi = {
|
|||||||
formData.append('file', file);
|
formData.append('file', file);
|
||||||
formData.append('media_type', mediaType);
|
formData.append('media_type', mediaType);
|
||||||
|
|
||||||
const response = await apiClient.post<MediaUploadResponse>('/cabinet/media/upload', formData, {
|
const response = await apiClient.post<MediaUploadResponse>('/cabinet/media/upload', formData);
|
||||||
headers: {
|
|
||||||
'Content-Type': 'multipart/form-data',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -163,11 +163,7 @@ export const adminPinnedMessagesApi = {
|
|||||||
formData.append('file', file);
|
formData.append('file', file);
|
||||||
formData.append('media_type', mediaType);
|
formData.append('media_type', mediaType);
|
||||||
|
|
||||||
const response = await apiClient.post<MediaUploadResponse>('/cabinet/media/upload', formData, {
|
const response = await apiClient.post<MediaUploadResponse>('/cabinet/media/upload', formData);
|
||||||
headers: {
|
|
||||||
'Content-Type': 'multipart/form-data',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -153,11 +153,7 @@ export const brandingApi = {
|
|||||||
uploadLogo: async (file: File): Promise<BrandingInfo> => {
|
uploadLogo: async (file: File): Promise<BrandingInfo> => {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('file', file);
|
formData.append('file', file);
|
||||||
const response = await apiClient.post<BrandingInfo>('/cabinet/branding/logo', formData, {
|
const response = await apiClient.post<BrandingInfo>('/cabinet/branding/logo', formData);
|
||||||
headers: {
|
|
||||||
'Content-Type': 'multipart/form-data',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
// Invalidate cached blob so it gets re-fetched
|
// Invalidate cached blob so it gets re-fetched
|
||||||
if (_logoBlobUrl) {
|
if (_logoBlobUrl) {
|
||||||
URL.revokeObjectURL(_logoBlobUrl);
|
URL.revokeObjectURL(_logoBlobUrl);
|
||||||
|
|||||||
@@ -82,6 +82,11 @@ function isAuthEndpoint(url: string | undefined): boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
apiClient.interceptors.request.use(async (config: InternalAxiosRequestConfig) => {
|
apiClient.interceptors.request.use(async (config: InternalAxiosRequestConfig) => {
|
||||||
|
// Let axios set the correct multipart/form-data header with boundary for FormData
|
||||||
|
if (config.data instanceof FormData && config.headers) {
|
||||||
|
delete config.headers['Content-Type'];
|
||||||
|
}
|
||||||
|
|
||||||
if (!isAuthEndpoint(config.url)) {
|
if (!isAuthEndpoint(config.url)) {
|
||||||
let token = tokenStorage.getAccessToken();
|
let token = tokenStorage.getAccessToken();
|
||||||
|
|
||||||
|
|||||||
@@ -66,10 +66,7 @@ export const newsApi = {
|
|||||||
const response = await apiClient.post<NewsMediaUploadResponse>(
|
const response = await apiClient.post<NewsMediaUploadResponse>(
|
||||||
'/cabinet/admin/news/media/upload',
|
'/cabinet/admin/news/media/upload',
|
||||||
formData,
|
formData,
|
||||||
{
|
{ signal },
|
||||||
headers: { 'Content-Type': 'multipart/form-data' },
|
|
||||||
signal,
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -68,11 +68,7 @@ export const ticketsApi = {
|
|||||||
formData.append('file', file);
|
formData.append('file', file);
|
||||||
formData.append('media_type', mediaType);
|
formData.append('media_type', mediaType);
|
||||||
|
|
||||||
const response = await apiClient.post<MediaUploadResponse>('/cabinet/media/upload', formData, {
|
const response = await apiClient.post<MediaUploadResponse>('/cabinet/media/upload', formData);
|
||||||
headers: {
|
|
||||||
'Content-Type': 'multipart/form-data',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -430,7 +430,7 @@ export default function AdminNewsCreate() {
|
|||||||
const handleCreateCategory = useCallback(
|
const handleCreateCategory = useCallback(
|
||||||
async (name: string, color: string) => {
|
async (name: string, color: string) => {
|
||||||
const cat = await newsApi.createCategory({ name, color });
|
const cat = await newsApi.createCategory({ name, color });
|
||||||
queryClient.invalidateQueries({ queryKey: ['admin', 'news', 'categories'] });
|
await queryClient.invalidateQueries({ queryKey: ['admin', 'news', 'categories'] });
|
||||||
return cat;
|
return cat;
|
||||||
},
|
},
|
||||||
[queryClient],
|
[queryClient],
|
||||||
@@ -439,7 +439,7 @@ export default function AdminNewsCreate() {
|
|||||||
const handleCreateTag = useCallback(
|
const handleCreateTag = useCallback(
|
||||||
async (name: string, color: string) => {
|
async (name: string, color: string) => {
|
||||||
const tag = await newsApi.createTag({ name, color });
|
const tag = await newsApi.createTag({ name, color });
|
||||||
queryClient.invalidateQueries({ queryKey: ['admin', 'news', 'tags'] });
|
await queryClient.invalidateQueries({ queryKey: ['admin', 'news', 'tags'] });
|
||||||
return tag;
|
return tag;
|
||||||
},
|
},
|
||||||
[queryClient],
|
[queryClient],
|
||||||
@@ -475,10 +475,11 @@ export default function AdminNewsCreate() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (articleData.tag) {
|
if (articleData.tag) {
|
||||||
|
const matchedTag = tagsData?.find((t) => t.id === articleData.tag_id);
|
||||||
setSelectedTag({
|
setSelectedTag({
|
||||||
id: articleData.tag_id ?? 0,
|
id: articleData.tag_id ?? 0,
|
||||||
name: articleData.tag,
|
name: articleData.tag,
|
||||||
color: articleData.category_color,
|
color: matchedTag?.color ?? '#94a3b8',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
setExcerpt(articleData.excerpt ?? '');
|
setExcerpt(articleData.excerpt ?? '');
|
||||||
@@ -490,7 +491,7 @@ export default function AdminNewsCreate() {
|
|||||||
editor.commands.setContent(articleData.content);
|
editor.commands.setContent(articleData.content);
|
||||||
editorPopulated.current = true;
|
editorPopulated.current = true;
|
||||||
}
|
}
|
||||||
}, [articleData, editor]);
|
}, [articleData, editor, tagsData]);
|
||||||
|
|
||||||
// Auto-generate slug from title
|
// Auto-generate slug from title
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -531,9 +532,9 @@ export default function AdminNewsCreate() {
|
|||||||
excerpt: excerpt.trim() || null,
|
excerpt: excerpt.trim() || null,
|
||||||
category: selectedCategory.name,
|
category: selectedCategory.name,
|
||||||
category_color: selectedCategory.color,
|
category_color: selectedCategory.color,
|
||||||
category_id: selectedCategory.id || null,
|
category_id: selectedCategory.id !== 0 ? selectedCategory.id : null,
|
||||||
tag: selectedTag?.name || null,
|
tag: selectedTag?.name ?? null,
|
||||||
tag_id: selectedTag?.id || null,
|
tag_id: selectedTag?.id != null && selectedTag.id !== 0 ? selectedTag.id : null,
|
||||||
featured_image_url: isSafeUrl(featuredImageUrl.trim()) ? featuredImageUrl.trim() : null,
|
featured_image_url: isSafeUrl(featuredImageUrl.trim()) ? featuredImageUrl.trim() : null,
|
||||||
is_published: isPublished,
|
is_published: isPublished,
|
||||||
is_featured: isFeatured,
|
is_featured: isFeatured,
|
||||||
|
|||||||
Reference in New Issue
Block a user