Align CMS title fallbacks and mutation error handling.

Add blog post untitled helper, use product title helper in order views, and surface API validation messages on product save/delete.
This commit is contained in:
2026-09-16 16:20:52 +02:00
parent 09ad236cb9
commit 0cd7158f7d
5 changed files with 18 additions and 7 deletions
+3 -2
View File
@@ -15,7 +15,7 @@
target="_blank" target="_blank"
rel="noopener" rel="noopener"
> >
{{ row.productTitle }} {{ getProductTitle(row.productTitle) }}
</router-link> </router-link>
</template> </template>
</el-table-column> </el-table-column>
@@ -113,6 +113,7 @@ import type { OrderExtended } from '@/types/order/OrderExtended';
import { DeliveryMode } from '@/types/product/DeliveryMode'; import { DeliveryMode } from '@/types/product/DeliveryMode';
import { formatDeliveryMode } from '@/utils/product/formatDeliveryMode'; import { formatDeliveryMode } from '@/utils/product/formatDeliveryMode';
import { formatFiatPrice } from '@/utils/formatFiatPrice'; import { formatFiatPrice } from '@/utils/formatFiatPrice';
import { getProductTitle } from '@/utils/product/getProductTitle';
import { resolveAxiosErrorMessage } from '@/utils/resolveAxiosErrorMessage'; import { resolveAxiosErrorMessage } from '@/utils/resolveAxiosErrorMessage';
const props = defineProps({ const props = defineProps({
@@ -143,7 +144,7 @@ const openAutoDelivery = (line: OrderLine): void => {
const markAsFulfilled = async (line: OrderLine): Promise<void> => { const markAsFulfilled = async (line: OrderLine): Promise<void> => {
try { try {
await ElMessageBox.confirm( await ElMessageBox.confirm(
`Mark "${line.productTitle}" (${line.variantTitle}) as fulfilled? This cannot be undone.`, `Mark "${getProductTitle(line.productTitle)}" (${line.variantTitle}) as fulfilled? This cannot be undone.`,
'Mark as fulfilled', 'Mark as fulfilled',
{ {
type: 'warning', type: 'warning',
@@ -70,6 +70,7 @@ import type { OrderLine } from '@/types/order/OrderLine';
import type { OrderLineAutoFulfillmentItem } from '@/types/order/OrderLineAutoFulfillmentItem'; import type { OrderLineAutoFulfillmentItem } from '@/types/order/OrderLineAutoFulfillmentItem';
import type { OrderLineAutoFulfillmentItemAttachment } from '@/types/order/OrderLineAutoFulfillmentItemAttachment'; import type { OrderLineAutoFulfillmentItemAttachment } from '@/types/order/OrderLineAutoFulfillmentItemAttachment';
import { formatFileSize } from '@/utils/formatFileSize'; import { formatFileSize } from '@/utils/formatFileSize';
import { getProductTitle } from '@/utils/product/getProductTitle';
const props = defineProps({ const props = defineProps({
modelValue: { modelValue: {
@@ -99,7 +100,7 @@ const lineSummary = computed((): string => {
return ''; return '';
} }
return `${line.productTitle} · ${line.variantTitle}`; return `${getProductTitle(line.productTitle)} · ${line.variantTitle}`;
}); });
const downloadAttachment = async ( const downloadAttachment = async (
+1
View File
@@ -0,0 +1 @@
export const UNTITLED_BLOG_POST_TITLE = '(untitled)';
+3
View File
@@ -0,0 +1,3 @@
import { UNTITLED_BLOG_POST_TITLE } from '@/consts/untitledBlogPostTitle';
export const getBlogPostTitle = (title: string | undefined | null): string => title || UNTITLED_BLOG_POST_TITLE;
+9 -4
View File
@@ -131,6 +131,7 @@ import { useCategoriesStore } from '@/stores/categories';
import { useProductsStore } from '@/stores/products'; import { useProductsStore } from '@/stores/products';
import type { ProductVariantExtended } from '@/types/product/ProductVariantExtended'; import type { ProductVariantExtended } from '@/types/product/ProductVariantExtended';
import { formatFiatPrice } from '@/utils/formatFiatPrice'; import { formatFiatPrice } from '@/utils/formatFiatPrice';
import { resolveAxiosErrorMessage } from '@/utils/resolveAxiosErrorMessage';
import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'element-plus'; import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'element-plus';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { computed, onBeforeMount, reactive, ref } from 'vue'; import { computed, onBeforeMount, reactive, ref } from 'vue';
@@ -241,8 +242,10 @@ const submitProduct = async (): Promise<void> => {
}); });
ElMessage.success('Product saved'); ElMessage.success('Product saved');
} catch { } catch (e) {
ElMessage.error('Could not save product'); const message = resolveAxiosErrorMessage(e, 'Could not save product');
ElMessage.error(message);
} finally { } finally {
productSaving.value = false; productSaving.value = false;
} }
@@ -288,8 +291,10 @@ const confirmDeleteProduct = async (): Promise<void> => {
await productsStore.deleteProduct(pid); await productsStore.deleteProduct(pid);
ElMessage.success('Product deleted'); ElMessage.success('Product deleted');
router.push({ name: ROUTE_NAMES.Products }); router.push({ name: ROUTE_NAMES.Products });
} catch { } catch (e) {
ElMessage.error('Could not delete product'); const message = resolveAxiosErrorMessage(e, 'Could not delete product');
ElMessage.error(message);
} finally { } finally {
deleteSaving.value = false; deleteSaving.value = false;
} }