Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2b12749997 | ||
|
|
e25962fd90 | ||
|
|
06ed5ed8e1 |
@@ -34,20 +34,42 @@ let nextTemporaryCollectionId = -1;
|
||||
|
||||
const canView = computed(canViewCustomerRuleConfiguration);
|
||||
const canManage = computed(canManageCustomerRuleConfiguration);
|
||||
const productRuleDefinitions = computed(() => (
|
||||
const productRuleDefinitions = computed(() =>
|
||||
getCustomerRuleDefinitions().filter((definition) => definition.productImpact)
|
||||
));
|
||||
);
|
||||
const productRuleTranslations = computed(() => ({
|
||||
restrictAdditionalServices: {
|
||||
label: t("customer_rules.attributes.restrictAdditionalServices.label"),
|
||||
description: t("customer_rules.attributes.restrictAdditionalServices.description"),
|
||||
},
|
||||
restrictTankCleaning: {
|
||||
label: t("customer_rules.attributes.restrictTankCleaning.label"),
|
||||
description: t("customer_rules.attributes.restrictTankCleaning.description"),
|
||||
},
|
||||
restrictSpotFree: {
|
||||
label: t("customer_rules.attributes.restrictSpotFree.label"),
|
||||
description: t("customer_rules.attributes.restrictSpotFree.description"),
|
||||
},
|
||||
restrictInteriorCleaning: {
|
||||
label: t("customer_rules.attributes.restrictInteriorCleaning.label"),
|
||||
description: t("customer_rules.attributes.restrictInteriorCleaning.description"),
|
||||
},
|
||||
onlyTankCleaning: {
|
||||
label: t("customer_rules.attributes.onlyTankCleaning.label"),
|
||||
description: t("customer_rules.attributes.onlyTankCleaning.description"),
|
||||
},
|
||||
}));
|
||||
const productRuleTranslation = (attribute, field) => productRuleTranslations.value[attribute]?.[field] ?? attribute;
|
||||
|
||||
const normalizedSnapshot = (rule) => JSON.stringify(serializeCustomerRuleProductRestriction(rule));
|
||||
const isSaving = (attribute) => savingAttributes.value.has(attribute);
|
||||
const hasConflict = (attribute) => conflictAttributes.value.has(attribute);
|
||||
const isDirty = (rule) => savedSnapshots.value.get(rule.attribute) !== normalizedSnapshot(rule);
|
||||
const effectiveDisabledProductIds = (rule) => (
|
||||
[...new Set(rule.collections.flatMap((collection) => collection.product_ids))]
|
||||
);
|
||||
const productIsArchived = (product) => (
|
||||
product?.deleted_at != null || product?.active === false || product?.is_active === false
|
||||
);
|
||||
const effectiveDisabledProductIds = (rule) => [
|
||||
...new Set(rule.collections.flatMap((collection) => collection.product_ids)),
|
||||
];
|
||||
const productIsArchived = (product) =>
|
||||
product?.deleted_at != null || product?.active === false || product?.is_active === false;
|
||||
const productLabel = (product) => {
|
||||
const category = String(product?.category_name ?? product?.category?.name ?? "").trim();
|
||||
return category ? `${product.name} · ${category}` : product.name;
|
||||
@@ -56,21 +78,19 @@ const productLabel = (product) => {
|
||||
const normalizeLoadedRules = (response) => {
|
||||
const normalized = normalizeCustomerRuleProductRestrictionResponse(response);
|
||||
const byAttribute = new Map(normalized.rules.map((rule) => [rule.attribute, rule]));
|
||||
const nextDrafts = productRuleDefinitions.value.map((definition) => (
|
||||
byAttribute.get(definition.attribute)
|
||||
?? normalizeCustomerRuleProductRestriction({ attribute: definition.attribute, version: 0, collections: [] })
|
||||
));
|
||||
const nextDrafts = productRuleDefinitions.value.map(
|
||||
(definition) =>
|
||||
byAttribute.get(definition.attribute) ??
|
||||
normalizeCustomerRuleProductRestriction({ attribute: definition.attribute, version: 0, collections: [] })
|
||||
);
|
||||
|
||||
drafts.value = nextDrafts;
|
||||
products.value = normalized.products.sort((left, right) => left.name.localeCompare(right.name));
|
||||
savedSnapshots.value = new Map(nextDrafts.map((rule) => [rule.attribute, normalizedSnapshot(rule)]));
|
||||
};
|
||||
|
||||
const parseError = (error, fallbackKey) => (
|
||||
error?.response?.data?.data?.message
|
||||
|| error?.response?.data?.message
|
||||
|| t(fallbackKey)
|
||||
);
|
||||
const parseError = (error, fallbackMessage) =>
|
||||
error?.response?.data?.data?.message || error?.response?.data?.message || fallbackMessage;
|
||||
|
||||
const load = async () => {
|
||||
if (!canView.value) {
|
||||
@@ -87,7 +107,7 @@ const load = async () => {
|
||||
conflictAttributes.value = new Set();
|
||||
} catch (error) {
|
||||
console.error("Failed to load customer rule product restrictions", error);
|
||||
loadError.value = parseError(error, "customer_rules.configuration.errors.load");
|
||||
loadError.value = parseError(error, t("customer_rules.configuration.errors.load"));
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
@@ -107,10 +127,9 @@ const visibleProducts = (rule, collection) => {
|
||||
return products.value;
|
||||
}
|
||||
|
||||
return products.value.filter((product) => (
|
||||
productLabel(product).toLocaleLowerCase().includes(search)
|
||||
|| String(product.id).includes(search)
|
||||
));
|
||||
return products.value.filter(
|
||||
(product) => productLabel(product).toLocaleLowerCase().includes(search) || String(product.id).includes(search)
|
||||
);
|
||||
};
|
||||
|
||||
const isProductSelected = (collection, productId) => collection.product_ids.includes(productId);
|
||||
@@ -222,7 +241,7 @@ const save = async (rule) => {
|
||||
} else {
|
||||
saveErrors.value = {
|
||||
...saveErrors.value,
|
||||
[rule.attribute]: parseError(error, "customer_rules.configuration.errors.save"),
|
||||
[rule.attribute]: parseError(error, t("customer_rules.configuration.errors.save")),
|
||||
};
|
||||
}
|
||||
} finally {
|
||||
@@ -239,10 +258,13 @@ const cancel = (rule) => {
|
||||
}
|
||||
|
||||
const saved = JSON.parse(snapshot);
|
||||
replaceSavedRule(rule.attribute, normalizeCustomerRuleProductRestriction({
|
||||
replaceSavedRule(
|
||||
rule.attribute,
|
||||
normalizeCustomerRuleProductRestriction({
|
||||
attribute: rule.attribute,
|
||||
...saved,
|
||||
}));
|
||||
})
|
||||
);
|
||||
saveErrors.value = { ...saveErrors.value, [rule.attribute]: "" };
|
||||
};
|
||||
|
||||
@@ -288,8 +310,10 @@ watch(
|
||||
>
|
||||
<div class="customer-rule-configuration__rule-header">
|
||||
<div>
|
||||
<h2 class="title is-5 mb-1">{{ t(`customer_rules.attributes.${rule.attribute}.label`) }}</h2>
|
||||
<p class="is-size-7 has-text-grey">{{ t(`customer_rules.attributes.${rule.attribute}.description`) }}</p>
|
||||
<h2 class="title is-5 mb-1">{{ productRuleTranslation(rule.attribute, "label") }}</h2>
|
||||
<p class="is-size-7 has-text-grey">
|
||||
{{ productRuleTranslation(rule.attribute, "description") }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="tags">
|
||||
<span class="tag is-light">{{ t("customer_rules.configuration.version", { version: rule.version }) }}</span>
|
||||
|
||||
@@ -361,7 +361,10 @@ test.describe("admin POS order filters", () => {
|
||||
});
|
||||
|
||||
test("preserves the department and draft customer across filter changes", async ({ page }, testInfo) => {
|
||||
test.skip(testInfo.project.name.includes("mobile"), "Desktop draft filter coverage");
|
||||
test.skip(
|
||||
testInfo.project.name.includes("mobile") || testInfo.project.name.includes("tablet"),
|
||||
"Desktop draft filter coverage"
|
||||
);
|
||||
|
||||
await page.clock.setFixedTime(new Date("2026-07-07T10:00:00.000Z"));
|
||||
const requests = await setupAdminOrderFilters(page);
|
||||
|
||||
Reference in New Issue
Block a user