When an Owner is selected, the Customer filter options will update to show the corresponding customers, and vice versa
This commit is contained in:
40
dist/chromium/content.js
vendored
40
dist/chromium/content.js
vendored
@@ -2601,6 +2601,7 @@ function createContentModal({
|
|||||||
opportunitiesFilterState.hasSavedCustomerFilter = true;
|
opportunitiesFilterState.hasSavedCustomerFilter = true;
|
||||||
saveFilterPreferences();
|
saveFilterPreferences();
|
||||||
renderCustomerFilter();
|
renderCustomerFilter();
|
||||||
|
renderOwnerFilter();
|
||||||
renderOpportunitiesTable();
|
renderOpportunitiesTable();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2661,6 +2662,7 @@ function createContentModal({
|
|||||||
opportunitiesFilterState.hasSavedOwnerFilter = true;
|
opportunitiesFilterState.hasSavedOwnerFilter = true;
|
||||||
saveFilterPreferences();
|
saveFilterPreferences();
|
||||||
renderOwnerFilter();
|
renderOwnerFilter();
|
||||||
|
renderCustomerFilter();
|
||||||
renderOpportunitiesTable();
|
renderOpportunitiesTable();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -7053,21 +7055,35 @@ function createOpportunitiesFilters({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keep stored selections intact: narrowing a menu must not turn an implicit
|
||||||
|
// "all" into a second filter that prevents expanding the original selection.
|
||||||
|
function getAvailableCustomerOptions() {
|
||||||
|
const items = opportunitiesTableState.opportunitiesTableState.items;
|
||||||
|
const allOwners = getOwnerOptions().every(option => opportunitiesFilterState.selectedOwnerKeys.has(option.key));
|
||||||
|
return getCustomerOptions(allOwners ? items : items.filter(item => opportunitiesFilterState.selectedOwnerKeys.has(getOwnerKey(item))));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAvailableOwnerOptions() {
|
||||||
|
const items = opportunitiesTableState.opportunitiesTableState.items;
|
||||||
|
const allCustomers = getCustomerOptions().every(option => opportunitiesFilterState.selectedCustomerKeys.has(option.key));
|
||||||
|
return getOwnerOptions(allCustomers ? items : items.filter(item => opportunitiesFilterState.selectedCustomerKeys.has(getCustomerKey(item))));
|
||||||
|
}
|
||||||
|
|
||||||
function renderCustomerFilter() {
|
function renderCustomerFilter() {
|
||||||
const trigger = document.getElementById(CUSTOMER_FILTER_BUTTON_ID);
|
const trigger = document.getElementById(CUSTOMER_FILTER_BUTTON_ID);
|
||||||
const searchInput = document.getElementById(CUSTOMER_FILTER_SEARCH_ID);
|
const searchInput = document.getElementById(CUSTOMER_FILTER_SEARCH_ID);
|
||||||
const selectAllCheckbox = document.getElementById(CUSTOMER_FILTER_SELECT_ALL_ID);
|
const selectAllCheckbox = document.getElementById(CUSTOMER_FILTER_SELECT_ALL_ID);
|
||||||
const customerOptions = getCustomerOptions();
|
const customerOptions = getAvailableCustomerOptions();
|
||||||
|
|
||||||
if (!trigger || !searchInput || !selectAllCheckbox) {
|
if (!trigger || !searchInput || !selectAllCheckbox) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedCount = customerOptions.filter((customer) => opportunitiesFilterState.selectedCustomerKeys.has(customer.key)).length;
|
const selectedCount = customerOptions.filter((customer) => opportunitiesFilterState.selectedCustomerKeys.has(customer.key)).length;
|
||||||
trigger.disabled = customerOptions.length === 0;
|
trigger.disabled = getCustomerOptions().length === 0;
|
||||||
trigger.textContent = getCustomerFilterSummary(customerOptions.length, selectedCount);
|
trigger.textContent = getCustomerFilterSummary(customerOptions.length, selectedCount);
|
||||||
searchInput.value = opportunitiesFilterState.customerSearch;
|
searchInput.value = opportunitiesFilterState.customerSearch;
|
||||||
selectAllCheckbox.disabled = customerOptions.length === 0;
|
selectAllCheckbox.disabled = getCustomerOptions().length === 0;
|
||||||
selectAllCheckbox.checked = customerOptions.length > 0 && selectedCount === customerOptions.length;
|
selectAllCheckbox.checked = customerOptions.length > 0 && selectedCount === customerOptions.length;
|
||||||
selectAllCheckbox.indeterminate = selectedCount > 0 && selectedCount < customerOptions.length;
|
selectAllCheckbox.indeterminate = selectedCount > 0 && selectedCount < customerOptions.length;
|
||||||
renderCustomerFilterList();
|
renderCustomerFilterList();
|
||||||
@@ -7081,7 +7097,7 @@ function createOpportunitiesFilters({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const normalizedSearch = opportunitiesFilterState.customerSearch.trim().toLocaleLowerCase();
|
const normalizedSearch = opportunitiesFilterState.customerSearch.trim().toLocaleLowerCase();
|
||||||
const customerOptions = getCustomerOptions().filter((customer) => {
|
const customerOptions = getAvailableCustomerOptions().filter((customer) => {
|
||||||
return customer.label.toLocaleLowerCase().includes(normalizedSearch);
|
return customer.label.toLocaleLowerCase().includes(normalizedSearch);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -7103,7 +7119,7 @@ function createOpportunitiesFilters({
|
|||||||
checkbox.type = "checkbox";
|
checkbox.type = "checkbox";
|
||||||
checkbox.checked = opportunitiesFilterState.selectedCustomerKeys.has(customer.key);
|
checkbox.checked = opportunitiesFilterState.selectedCustomerKeys.has(customer.key);
|
||||||
checkbox.addEventListener("change", () => {
|
checkbox.addEventListener("change", () => {
|
||||||
if (getCustomerOptions().every(option => opportunitiesFilterState.selectedCustomerKeys.has(option.key))) {
|
if (getAvailableCustomerOptions().every(option => opportunitiesFilterState.selectedCustomerKeys.has(option.key))) {
|
||||||
opportunitiesFilterState.selectedCustomerKeys.clear();
|
opportunitiesFilterState.selectedCustomerKeys.clear();
|
||||||
opportunitiesFilterState.selectedCustomerKeys.add(customer.key);
|
opportunitiesFilterState.selectedCustomerKeys.add(customer.key);
|
||||||
} else if (checkbox.checked) {
|
} else if (checkbox.checked) {
|
||||||
@@ -7115,6 +7131,7 @@ function createOpportunitiesFilters({
|
|||||||
opportunitiesFilterState.hasSavedCustomerFilter = true;
|
opportunitiesFilterState.hasSavedCustomerFilter = true;
|
||||||
saveFilterPreferences();
|
saveFilterPreferences();
|
||||||
renderCustomerFilter();
|
renderCustomerFilter();
|
||||||
|
renderOwnerFilter();
|
||||||
renderOpportunitiesTable();
|
renderOpportunitiesTable();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -7129,18 +7146,18 @@ function createOpportunitiesFilters({
|
|||||||
const trigger = document.getElementById(OWNER_FILTER_BUTTON_ID);
|
const trigger = document.getElementById(OWNER_FILTER_BUTTON_ID);
|
||||||
const searchInput = document.getElementById(OWNER_FILTER_SEARCH_ID);
|
const searchInput = document.getElementById(OWNER_FILTER_SEARCH_ID);
|
||||||
const selectAllCheckbox = document.getElementById(OWNER_FILTER_SELECT_ALL_ID);
|
const selectAllCheckbox = document.getElementById(OWNER_FILTER_SELECT_ALL_ID);
|
||||||
const ownerOptions = getOwnerOptions();
|
const ownerOptions = getAvailableOwnerOptions();
|
||||||
|
|
||||||
if (!trigger || !searchInput || !selectAllCheckbox) {
|
if (!trigger || !searchInput || !selectAllCheckbox) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedCount = ownerOptions.filter((owner) => opportunitiesFilterState.selectedOwnerKeys.has(owner.key)).length;
|
const selectedCount = ownerOptions.filter((owner) => opportunitiesFilterState.selectedOwnerKeys.has(owner.key)).length;
|
||||||
trigger.disabled = ownerOptions.length === 0;
|
trigger.disabled = getOwnerOptions().length === 0;
|
||||||
trigger.textContent = getCustomerFilterSummary(ownerOptions.length, selectedCount)
|
trigger.textContent = getCustomerFilterSummary(ownerOptions.length, selectedCount)
|
||||||
.replace("customer", "owner");
|
.replace("customer", "owner");
|
||||||
searchInput.value = opportunitiesFilterState.ownerSearch;
|
searchInput.value = opportunitiesFilterState.ownerSearch;
|
||||||
selectAllCheckbox.disabled = ownerOptions.length === 0;
|
selectAllCheckbox.disabled = getOwnerOptions().length === 0;
|
||||||
selectAllCheckbox.checked = ownerOptions.length > 0 && selectedCount === ownerOptions.length;
|
selectAllCheckbox.checked = ownerOptions.length > 0 && selectedCount === ownerOptions.length;
|
||||||
selectAllCheckbox.indeterminate = selectedCount > 0 && selectedCount < ownerOptions.length;
|
selectAllCheckbox.indeterminate = selectedCount > 0 && selectedCount < ownerOptions.length;
|
||||||
renderOwnerFilterList();
|
renderOwnerFilterList();
|
||||||
@@ -7154,7 +7171,7 @@ function createOpportunitiesFilters({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const normalizedSearch = opportunitiesFilterState.ownerSearch.trim().toLocaleLowerCase();
|
const normalizedSearch = opportunitiesFilterState.ownerSearch.trim().toLocaleLowerCase();
|
||||||
const ownerOptions = getOwnerOptions().filter((owner) => {
|
const ownerOptions = getAvailableOwnerOptions().filter((owner) => {
|
||||||
return owner.label.toLocaleLowerCase().includes(normalizedSearch);
|
return owner.label.toLocaleLowerCase().includes(normalizedSearch);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -7176,7 +7193,7 @@ function createOpportunitiesFilters({
|
|||||||
checkbox.type = "checkbox";
|
checkbox.type = "checkbox";
|
||||||
checkbox.checked = opportunitiesFilterState.selectedOwnerKeys.has(owner.key);
|
checkbox.checked = opportunitiesFilterState.selectedOwnerKeys.has(owner.key);
|
||||||
checkbox.addEventListener("change", () => {
|
checkbox.addEventListener("change", () => {
|
||||||
if (getOwnerOptions().every(option => opportunitiesFilterState.selectedOwnerKeys.has(option.key))) {
|
if (getAvailableOwnerOptions().every(option => opportunitiesFilterState.selectedOwnerKeys.has(option.key))) {
|
||||||
opportunitiesFilterState.selectedOwnerKeys.clear();
|
opportunitiesFilterState.selectedOwnerKeys.clear();
|
||||||
opportunitiesFilterState.selectedOwnerKeys.add(owner.key);
|
opportunitiesFilterState.selectedOwnerKeys.add(owner.key);
|
||||||
} else if (checkbox.checked) {
|
} else if (checkbox.checked) {
|
||||||
@@ -7188,6 +7205,7 @@ function createOpportunitiesFilters({
|
|||||||
opportunitiesFilterState.hasSavedOwnerFilter = true;
|
opportunitiesFilterState.hasSavedOwnerFilter = true;
|
||||||
saveFilterPreferences();
|
saveFilterPreferences();
|
||||||
renderOwnerFilter();
|
renderOwnerFilter();
|
||||||
|
renderCustomerFilter();
|
||||||
renderOpportunitiesTable();
|
renderOpportunitiesTable();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -7269,7 +7287,7 @@ function createOpportunitiesFilters({
|
|||||||
return opportunitiesFilterState.selectedStatuses.has(statusCode);
|
return opportunitiesFilterState.selectedStatuses.has(statusCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { resetCustomerAndOwnerFiltersForPeriod, renderStageFilterButtons, toggleStageFilter, renderStatusFilterButtons, toggleStatusFilter, toggleCustomerFilterPanel, closeCustomerFilterPanel, toggleOwnerFilterPanel, closeOwnerFilterPanel, resetCustomerFilter, resetOwnerFilter, renderCustomerFilter, renderCustomerFilterList, renderOwnerFilter, renderOwnerFilterList, getSortedOpportunities, matchesTableSearch, matchesSelectedStatus };
|
return { getAvailableCustomerOptions, getAvailableOwnerOptions, resetCustomerAndOwnerFiltersForPeriod, renderStageFilterButtons, toggleStageFilter, renderStatusFilterButtons, toggleStatusFilter, toggleCustomerFilterPanel, closeCustomerFilterPanel, toggleOwnerFilterPanel, closeOwnerFilterPanel, resetCustomerFilter, resetOwnerFilter, renderCustomerFilter, renderCustomerFilterList, renderOwnerFilter, renderOwnerFilterList, getSortedOpportunities, matchesTableSearch, matchesSelectedStatus };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Source: src/features/opportunities/formatters.js
|
// Source: src/features/opportunities/formatters.js
|
||||||
|
|||||||
40
dist/firefox/content.js
vendored
40
dist/firefox/content.js
vendored
@@ -2601,6 +2601,7 @@ function createContentModal({
|
|||||||
opportunitiesFilterState.hasSavedCustomerFilter = true;
|
opportunitiesFilterState.hasSavedCustomerFilter = true;
|
||||||
saveFilterPreferences();
|
saveFilterPreferences();
|
||||||
renderCustomerFilter();
|
renderCustomerFilter();
|
||||||
|
renderOwnerFilter();
|
||||||
renderOpportunitiesTable();
|
renderOpportunitiesTable();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2661,6 +2662,7 @@ function createContentModal({
|
|||||||
opportunitiesFilterState.hasSavedOwnerFilter = true;
|
opportunitiesFilterState.hasSavedOwnerFilter = true;
|
||||||
saveFilterPreferences();
|
saveFilterPreferences();
|
||||||
renderOwnerFilter();
|
renderOwnerFilter();
|
||||||
|
renderCustomerFilter();
|
||||||
renderOpportunitiesTable();
|
renderOpportunitiesTable();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -7053,21 +7055,35 @@ function createOpportunitiesFilters({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keep stored selections intact: narrowing a menu must not turn an implicit
|
||||||
|
// "all" into a second filter that prevents expanding the original selection.
|
||||||
|
function getAvailableCustomerOptions() {
|
||||||
|
const items = opportunitiesTableState.opportunitiesTableState.items;
|
||||||
|
const allOwners = getOwnerOptions().every(option => opportunitiesFilterState.selectedOwnerKeys.has(option.key));
|
||||||
|
return getCustomerOptions(allOwners ? items : items.filter(item => opportunitiesFilterState.selectedOwnerKeys.has(getOwnerKey(item))));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAvailableOwnerOptions() {
|
||||||
|
const items = opportunitiesTableState.opportunitiesTableState.items;
|
||||||
|
const allCustomers = getCustomerOptions().every(option => opportunitiesFilterState.selectedCustomerKeys.has(option.key));
|
||||||
|
return getOwnerOptions(allCustomers ? items : items.filter(item => opportunitiesFilterState.selectedCustomerKeys.has(getCustomerKey(item))));
|
||||||
|
}
|
||||||
|
|
||||||
function renderCustomerFilter() {
|
function renderCustomerFilter() {
|
||||||
const trigger = document.getElementById(CUSTOMER_FILTER_BUTTON_ID);
|
const trigger = document.getElementById(CUSTOMER_FILTER_BUTTON_ID);
|
||||||
const searchInput = document.getElementById(CUSTOMER_FILTER_SEARCH_ID);
|
const searchInput = document.getElementById(CUSTOMER_FILTER_SEARCH_ID);
|
||||||
const selectAllCheckbox = document.getElementById(CUSTOMER_FILTER_SELECT_ALL_ID);
|
const selectAllCheckbox = document.getElementById(CUSTOMER_FILTER_SELECT_ALL_ID);
|
||||||
const customerOptions = getCustomerOptions();
|
const customerOptions = getAvailableCustomerOptions();
|
||||||
|
|
||||||
if (!trigger || !searchInput || !selectAllCheckbox) {
|
if (!trigger || !searchInput || !selectAllCheckbox) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedCount = customerOptions.filter((customer) => opportunitiesFilterState.selectedCustomerKeys.has(customer.key)).length;
|
const selectedCount = customerOptions.filter((customer) => opportunitiesFilterState.selectedCustomerKeys.has(customer.key)).length;
|
||||||
trigger.disabled = customerOptions.length === 0;
|
trigger.disabled = getCustomerOptions().length === 0;
|
||||||
trigger.textContent = getCustomerFilterSummary(customerOptions.length, selectedCount);
|
trigger.textContent = getCustomerFilterSummary(customerOptions.length, selectedCount);
|
||||||
searchInput.value = opportunitiesFilterState.customerSearch;
|
searchInput.value = opportunitiesFilterState.customerSearch;
|
||||||
selectAllCheckbox.disabled = customerOptions.length === 0;
|
selectAllCheckbox.disabled = getCustomerOptions().length === 0;
|
||||||
selectAllCheckbox.checked = customerOptions.length > 0 && selectedCount === customerOptions.length;
|
selectAllCheckbox.checked = customerOptions.length > 0 && selectedCount === customerOptions.length;
|
||||||
selectAllCheckbox.indeterminate = selectedCount > 0 && selectedCount < customerOptions.length;
|
selectAllCheckbox.indeterminate = selectedCount > 0 && selectedCount < customerOptions.length;
|
||||||
renderCustomerFilterList();
|
renderCustomerFilterList();
|
||||||
@@ -7081,7 +7097,7 @@ function createOpportunitiesFilters({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const normalizedSearch = opportunitiesFilterState.customerSearch.trim().toLocaleLowerCase();
|
const normalizedSearch = opportunitiesFilterState.customerSearch.trim().toLocaleLowerCase();
|
||||||
const customerOptions = getCustomerOptions().filter((customer) => {
|
const customerOptions = getAvailableCustomerOptions().filter((customer) => {
|
||||||
return customer.label.toLocaleLowerCase().includes(normalizedSearch);
|
return customer.label.toLocaleLowerCase().includes(normalizedSearch);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -7103,7 +7119,7 @@ function createOpportunitiesFilters({
|
|||||||
checkbox.type = "checkbox";
|
checkbox.type = "checkbox";
|
||||||
checkbox.checked = opportunitiesFilterState.selectedCustomerKeys.has(customer.key);
|
checkbox.checked = opportunitiesFilterState.selectedCustomerKeys.has(customer.key);
|
||||||
checkbox.addEventListener("change", () => {
|
checkbox.addEventListener("change", () => {
|
||||||
if (getCustomerOptions().every(option => opportunitiesFilterState.selectedCustomerKeys.has(option.key))) {
|
if (getAvailableCustomerOptions().every(option => opportunitiesFilterState.selectedCustomerKeys.has(option.key))) {
|
||||||
opportunitiesFilterState.selectedCustomerKeys.clear();
|
opportunitiesFilterState.selectedCustomerKeys.clear();
|
||||||
opportunitiesFilterState.selectedCustomerKeys.add(customer.key);
|
opportunitiesFilterState.selectedCustomerKeys.add(customer.key);
|
||||||
} else if (checkbox.checked) {
|
} else if (checkbox.checked) {
|
||||||
@@ -7115,6 +7131,7 @@ function createOpportunitiesFilters({
|
|||||||
opportunitiesFilterState.hasSavedCustomerFilter = true;
|
opportunitiesFilterState.hasSavedCustomerFilter = true;
|
||||||
saveFilterPreferences();
|
saveFilterPreferences();
|
||||||
renderCustomerFilter();
|
renderCustomerFilter();
|
||||||
|
renderOwnerFilter();
|
||||||
renderOpportunitiesTable();
|
renderOpportunitiesTable();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -7129,18 +7146,18 @@ function createOpportunitiesFilters({
|
|||||||
const trigger = document.getElementById(OWNER_FILTER_BUTTON_ID);
|
const trigger = document.getElementById(OWNER_FILTER_BUTTON_ID);
|
||||||
const searchInput = document.getElementById(OWNER_FILTER_SEARCH_ID);
|
const searchInput = document.getElementById(OWNER_FILTER_SEARCH_ID);
|
||||||
const selectAllCheckbox = document.getElementById(OWNER_FILTER_SELECT_ALL_ID);
|
const selectAllCheckbox = document.getElementById(OWNER_FILTER_SELECT_ALL_ID);
|
||||||
const ownerOptions = getOwnerOptions();
|
const ownerOptions = getAvailableOwnerOptions();
|
||||||
|
|
||||||
if (!trigger || !searchInput || !selectAllCheckbox) {
|
if (!trigger || !searchInput || !selectAllCheckbox) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedCount = ownerOptions.filter((owner) => opportunitiesFilterState.selectedOwnerKeys.has(owner.key)).length;
|
const selectedCount = ownerOptions.filter((owner) => opportunitiesFilterState.selectedOwnerKeys.has(owner.key)).length;
|
||||||
trigger.disabled = ownerOptions.length === 0;
|
trigger.disabled = getOwnerOptions().length === 0;
|
||||||
trigger.textContent = getCustomerFilterSummary(ownerOptions.length, selectedCount)
|
trigger.textContent = getCustomerFilterSummary(ownerOptions.length, selectedCount)
|
||||||
.replace("customer", "owner");
|
.replace("customer", "owner");
|
||||||
searchInput.value = opportunitiesFilterState.ownerSearch;
|
searchInput.value = opportunitiesFilterState.ownerSearch;
|
||||||
selectAllCheckbox.disabled = ownerOptions.length === 0;
|
selectAllCheckbox.disabled = getOwnerOptions().length === 0;
|
||||||
selectAllCheckbox.checked = ownerOptions.length > 0 && selectedCount === ownerOptions.length;
|
selectAllCheckbox.checked = ownerOptions.length > 0 && selectedCount === ownerOptions.length;
|
||||||
selectAllCheckbox.indeterminate = selectedCount > 0 && selectedCount < ownerOptions.length;
|
selectAllCheckbox.indeterminate = selectedCount > 0 && selectedCount < ownerOptions.length;
|
||||||
renderOwnerFilterList();
|
renderOwnerFilterList();
|
||||||
@@ -7154,7 +7171,7 @@ function createOpportunitiesFilters({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const normalizedSearch = opportunitiesFilterState.ownerSearch.trim().toLocaleLowerCase();
|
const normalizedSearch = opportunitiesFilterState.ownerSearch.trim().toLocaleLowerCase();
|
||||||
const ownerOptions = getOwnerOptions().filter((owner) => {
|
const ownerOptions = getAvailableOwnerOptions().filter((owner) => {
|
||||||
return owner.label.toLocaleLowerCase().includes(normalizedSearch);
|
return owner.label.toLocaleLowerCase().includes(normalizedSearch);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -7176,7 +7193,7 @@ function createOpportunitiesFilters({
|
|||||||
checkbox.type = "checkbox";
|
checkbox.type = "checkbox";
|
||||||
checkbox.checked = opportunitiesFilterState.selectedOwnerKeys.has(owner.key);
|
checkbox.checked = opportunitiesFilterState.selectedOwnerKeys.has(owner.key);
|
||||||
checkbox.addEventListener("change", () => {
|
checkbox.addEventListener("change", () => {
|
||||||
if (getOwnerOptions().every(option => opportunitiesFilterState.selectedOwnerKeys.has(option.key))) {
|
if (getAvailableOwnerOptions().every(option => opportunitiesFilterState.selectedOwnerKeys.has(option.key))) {
|
||||||
opportunitiesFilterState.selectedOwnerKeys.clear();
|
opportunitiesFilterState.selectedOwnerKeys.clear();
|
||||||
opportunitiesFilterState.selectedOwnerKeys.add(owner.key);
|
opportunitiesFilterState.selectedOwnerKeys.add(owner.key);
|
||||||
} else if (checkbox.checked) {
|
} else if (checkbox.checked) {
|
||||||
@@ -7188,6 +7205,7 @@ function createOpportunitiesFilters({
|
|||||||
opportunitiesFilterState.hasSavedOwnerFilter = true;
|
opportunitiesFilterState.hasSavedOwnerFilter = true;
|
||||||
saveFilterPreferences();
|
saveFilterPreferences();
|
||||||
renderOwnerFilter();
|
renderOwnerFilter();
|
||||||
|
renderCustomerFilter();
|
||||||
renderOpportunitiesTable();
|
renderOpportunitiesTable();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -7269,7 +7287,7 @@ function createOpportunitiesFilters({
|
|||||||
return opportunitiesFilterState.selectedStatuses.has(statusCode);
|
return opportunitiesFilterState.selectedStatuses.has(statusCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { resetCustomerAndOwnerFiltersForPeriod, renderStageFilterButtons, toggleStageFilter, renderStatusFilterButtons, toggleStatusFilter, toggleCustomerFilterPanel, closeCustomerFilterPanel, toggleOwnerFilterPanel, closeOwnerFilterPanel, resetCustomerFilter, resetOwnerFilter, renderCustomerFilter, renderCustomerFilterList, renderOwnerFilter, renderOwnerFilterList, getSortedOpportunities, matchesTableSearch, matchesSelectedStatus };
|
return { getAvailableCustomerOptions, getAvailableOwnerOptions, resetCustomerAndOwnerFiltersForPeriod, renderStageFilterButtons, toggleStageFilter, renderStatusFilterButtons, toggleStatusFilter, toggleCustomerFilterPanel, closeCustomerFilterPanel, toggleOwnerFilterPanel, closeOwnerFilterPanel, resetCustomerFilter, resetOwnerFilter, renderCustomerFilter, renderCustomerFilterList, renderOwnerFilter, renderOwnerFilterList, getSortedOpportunities, matchesTableSearch, matchesSelectedStatus };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Source: src/features/opportunities/formatters.js
|
// Source: src/features/opportunities/formatters.js
|
||||||
|
|||||||
@@ -310,6 +310,7 @@ function createContentModal({
|
|||||||
opportunitiesFilterState.hasSavedCustomerFilter = true;
|
opportunitiesFilterState.hasSavedCustomerFilter = true;
|
||||||
saveFilterPreferences();
|
saveFilterPreferences();
|
||||||
renderCustomerFilter();
|
renderCustomerFilter();
|
||||||
|
renderOwnerFilter();
|
||||||
renderOpportunitiesTable();
|
renderOpportunitiesTable();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -370,6 +371,7 @@ function createContentModal({
|
|||||||
opportunitiesFilterState.hasSavedOwnerFilter = true;
|
opportunitiesFilterState.hasSavedOwnerFilter = true;
|
||||||
saveFilterPreferences();
|
saveFilterPreferences();
|
||||||
renderOwnerFilter();
|
renderOwnerFilter();
|
||||||
|
renderCustomerFilter();
|
||||||
renderOpportunitiesTable();
|
renderOpportunitiesTable();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -202,21 +202,35 @@ function createOpportunitiesFilters({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keep stored selections intact: narrowing a menu must not turn an implicit
|
||||||
|
// "all" into a second filter that prevents expanding the original selection.
|
||||||
|
function getAvailableCustomerOptions() {
|
||||||
|
const items = opportunitiesTableState.opportunitiesTableState.items;
|
||||||
|
const allOwners = getOwnerOptions().every(option => opportunitiesFilterState.selectedOwnerKeys.has(option.key));
|
||||||
|
return getCustomerOptions(allOwners ? items : items.filter(item => opportunitiesFilterState.selectedOwnerKeys.has(getOwnerKey(item))));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAvailableOwnerOptions() {
|
||||||
|
const items = opportunitiesTableState.opportunitiesTableState.items;
|
||||||
|
const allCustomers = getCustomerOptions().every(option => opportunitiesFilterState.selectedCustomerKeys.has(option.key));
|
||||||
|
return getOwnerOptions(allCustomers ? items : items.filter(item => opportunitiesFilterState.selectedCustomerKeys.has(getCustomerKey(item))));
|
||||||
|
}
|
||||||
|
|
||||||
function renderCustomerFilter() {
|
function renderCustomerFilter() {
|
||||||
const trigger = document.getElementById(CUSTOMER_FILTER_BUTTON_ID);
|
const trigger = document.getElementById(CUSTOMER_FILTER_BUTTON_ID);
|
||||||
const searchInput = document.getElementById(CUSTOMER_FILTER_SEARCH_ID);
|
const searchInput = document.getElementById(CUSTOMER_FILTER_SEARCH_ID);
|
||||||
const selectAllCheckbox = document.getElementById(CUSTOMER_FILTER_SELECT_ALL_ID);
|
const selectAllCheckbox = document.getElementById(CUSTOMER_FILTER_SELECT_ALL_ID);
|
||||||
const customerOptions = getCustomerOptions();
|
const customerOptions = getAvailableCustomerOptions();
|
||||||
|
|
||||||
if (!trigger || !searchInput || !selectAllCheckbox) {
|
if (!trigger || !searchInput || !selectAllCheckbox) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedCount = customerOptions.filter((customer) => opportunitiesFilterState.selectedCustomerKeys.has(customer.key)).length;
|
const selectedCount = customerOptions.filter((customer) => opportunitiesFilterState.selectedCustomerKeys.has(customer.key)).length;
|
||||||
trigger.disabled = customerOptions.length === 0;
|
trigger.disabled = getCustomerOptions().length === 0;
|
||||||
trigger.textContent = getCustomerFilterSummary(customerOptions.length, selectedCount);
|
trigger.textContent = getCustomerFilterSummary(customerOptions.length, selectedCount);
|
||||||
searchInput.value = opportunitiesFilterState.customerSearch;
|
searchInput.value = opportunitiesFilterState.customerSearch;
|
||||||
selectAllCheckbox.disabled = customerOptions.length === 0;
|
selectAllCheckbox.disabled = getCustomerOptions().length === 0;
|
||||||
selectAllCheckbox.checked = customerOptions.length > 0 && selectedCount === customerOptions.length;
|
selectAllCheckbox.checked = customerOptions.length > 0 && selectedCount === customerOptions.length;
|
||||||
selectAllCheckbox.indeterminate = selectedCount > 0 && selectedCount < customerOptions.length;
|
selectAllCheckbox.indeterminate = selectedCount > 0 && selectedCount < customerOptions.length;
|
||||||
renderCustomerFilterList();
|
renderCustomerFilterList();
|
||||||
@@ -230,7 +244,7 @@ function createOpportunitiesFilters({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const normalizedSearch = opportunitiesFilterState.customerSearch.trim().toLocaleLowerCase();
|
const normalizedSearch = opportunitiesFilterState.customerSearch.trim().toLocaleLowerCase();
|
||||||
const customerOptions = getCustomerOptions().filter((customer) => {
|
const customerOptions = getAvailableCustomerOptions().filter((customer) => {
|
||||||
return customer.label.toLocaleLowerCase().includes(normalizedSearch);
|
return customer.label.toLocaleLowerCase().includes(normalizedSearch);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -252,7 +266,7 @@ function createOpportunitiesFilters({
|
|||||||
checkbox.type = "checkbox";
|
checkbox.type = "checkbox";
|
||||||
checkbox.checked = opportunitiesFilterState.selectedCustomerKeys.has(customer.key);
|
checkbox.checked = opportunitiesFilterState.selectedCustomerKeys.has(customer.key);
|
||||||
checkbox.addEventListener("change", () => {
|
checkbox.addEventListener("change", () => {
|
||||||
if (getCustomerOptions().every(option => opportunitiesFilterState.selectedCustomerKeys.has(option.key))) {
|
if (getAvailableCustomerOptions().every(option => opportunitiesFilterState.selectedCustomerKeys.has(option.key))) {
|
||||||
opportunitiesFilterState.selectedCustomerKeys.clear();
|
opportunitiesFilterState.selectedCustomerKeys.clear();
|
||||||
opportunitiesFilterState.selectedCustomerKeys.add(customer.key);
|
opportunitiesFilterState.selectedCustomerKeys.add(customer.key);
|
||||||
} else if (checkbox.checked) {
|
} else if (checkbox.checked) {
|
||||||
@@ -264,6 +278,7 @@ function createOpportunitiesFilters({
|
|||||||
opportunitiesFilterState.hasSavedCustomerFilter = true;
|
opportunitiesFilterState.hasSavedCustomerFilter = true;
|
||||||
saveFilterPreferences();
|
saveFilterPreferences();
|
||||||
renderCustomerFilter();
|
renderCustomerFilter();
|
||||||
|
renderOwnerFilter();
|
||||||
renderOpportunitiesTable();
|
renderOpportunitiesTable();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -278,18 +293,18 @@ function createOpportunitiesFilters({
|
|||||||
const trigger = document.getElementById(OWNER_FILTER_BUTTON_ID);
|
const trigger = document.getElementById(OWNER_FILTER_BUTTON_ID);
|
||||||
const searchInput = document.getElementById(OWNER_FILTER_SEARCH_ID);
|
const searchInput = document.getElementById(OWNER_FILTER_SEARCH_ID);
|
||||||
const selectAllCheckbox = document.getElementById(OWNER_FILTER_SELECT_ALL_ID);
|
const selectAllCheckbox = document.getElementById(OWNER_FILTER_SELECT_ALL_ID);
|
||||||
const ownerOptions = getOwnerOptions();
|
const ownerOptions = getAvailableOwnerOptions();
|
||||||
|
|
||||||
if (!trigger || !searchInput || !selectAllCheckbox) {
|
if (!trigger || !searchInput || !selectAllCheckbox) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedCount = ownerOptions.filter((owner) => opportunitiesFilterState.selectedOwnerKeys.has(owner.key)).length;
|
const selectedCount = ownerOptions.filter((owner) => opportunitiesFilterState.selectedOwnerKeys.has(owner.key)).length;
|
||||||
trigger.disabled = ownerOptions.length === 0;
|
trigger.disabled = getOwnerOptions().length === 0;
|
||||||
trigger.textContent = getCustomerFilterSummary(ownerOptions.length, selectedCount)
|
trigger.textContent = getCustomerFilterSummary(ownerOptions.length, selectedCount)
|
||||||
.replace("customer", "owner");
|
.replace("customer", "owner");
|
||||||
searchInput.value = opportunitiesFilterState.ownerSearch;
|
searchInput.value = opportunitiesFilterState.ownerSearch;
|
||||||
selectAllCheckbox.disabled = ownerOptions.length === 0;
|
selectAllCheckbox.disabled = getOwnerOptions().length === 0;
|
||||||
selectAllCheckbox.checked = ownerOptions.length > 0 && selectedCount === ownerOptions.length;
|
selectAllCheckbox.checked = ownerOptions.length > 0 && selectedCount === ownerOptions.length;
|
||||||
selectAllCheckbox.indeterminate = selectedCount > 0 && selectedCount < ownerOptions.length;
|
selectAllCheckbox.indeterminate = selectedCount > 0 && selectedCount < ownerOptions.length;
|
||||||
renderOwnerFilterList();
|
renderOwnerFilterList();
|
||||||
@@ -303,7 +318,7 @@ function createOpportunitiesFilters({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const normalizedSearch = opportunitiesFilterState.ownerSearch.trim().toLocaleLowerCase();
|
const normalizedSearch = opportunitiesFilterState.ownerSearch.trim().toLocaleLowerCase();
|
||||||
const ownerOptions = getOwnerOptions().filter((owner) => {
|
const ownerOptions = getAvailableOwnerOptions().filter((owner) => {
|
||||||
return owner.label.toLocaleLowerCase().includes(normalizedSearch);
|
return owner.label.toLocaleLowerCase().includes(normalizedSearch);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -325,7 +340,7 @@ function createOpportunitiesFilters({
|
|||||||
checkbox.type = "checkbox";
|
checkbox.type = "checkbox";
|
||||||
checkbox.checked = opportunitiesFilterState.selectedOwnerKeys.has(owner.key);
|
checkbox.checked = opportunitiesFilterState.selectedOwnerKeys.has(owner.key);
|
||||||
checkbox.addEventListener("change", () => {
|
checkbox.addEventListener("change", () => {
|
||||||
if (getOwnerOptions().every(option => opportunitiesFilterState.selectedOwnerKeys.has(option.key))) {
|
if (getAvailableOwnerOptions().every(option => opportunitiesFilterState.selectedOwnerKeys.has(option.key))) {
|
||||||
opportunitiesFilterState.selectedOwnerKeys.clear();
|
opportunitiesFilterState.selectedOwnerKeys.clear();
|
||||||
opportunitiesFilterState.selectedOwnerKeys.add(owner.key);
|
opportunitiesFilterState.selectedOwnerKeys.add(owner.key);
|
||||||
} else if (checkbox.checked) {
|
} else if (checkbox.checked) {
|
||||||
@@ -337,6 +352,7 @@ function createOpportunitiesFilters({
|
|||||||
opportunitiesFilterState.hasSavedOwnerFilter = true;
|
opportunitiesFilterState.hasSavedOwnerFilter = true;
|
||||||
saveFilterPreferences();
|
saveFilterPreferences();
|
||||||
renderOwnerFilter();
|
renderOwnerFilter();
|
||||||
|
renderCustomerFilter();
|
||||||
renderOpportunitiesTable();
|
renderOpportunitiesTable();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -418,5 +434,5 @@ function createOpportunitiesFilters({
|
|||||||
return opportunitiesFilterState.selectedStatuses.has(statusCode);
|
return opportunitiesFilterState.selectedStatuses.has(statusCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { resetCustomerAndOwnerFiltersForPeriod, renderStageFilterButtons, toggleStageFilter, renderStatusFilterButtons, toggleStatusFilter, toggleCustomerFilterPanel, closeCustomerFilterPanel, toggleOwnerFilterPanel, closeOwnerFilterPanel, resetCustomerFilter, resetOwnerFilter, renderCustomerFilter, renderCustomerFilterList, renderOwnerFilter, renderOwnerFilterList, getSortedOpportunities, matchesTableSearch, matchesSelectedStatus };
|
return { getAvailableCustomerOptions, getAvailableOwnerOptions, resetCustomerAndOwnerFiltersForPeriod, renderStageFilterButtons, toggleStageFilter, renderStatusFilterButtons, toggleStatusFilter, toggleCustomerFilterPanel, closeCustomerFilterPanel, toggleOwnerFilterPanel, closeOwnerFilterPanel, resetCustomerFilter, resetOwnerFilter, renderCustomerFilter, renderCustomerFilterList, renderOwnerFilter, renderOwnerFilterList, getSortedOpportunities, matchesTableSearch, matchesSelectedStatus };
|
||||||
}
|
}
|
||||||
|
|||||||
43
tests/dependentFilters.test.cjs
Normal file
43
tests/dependentFilters.test.cjs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
const { test } = require('node:test');
|
||||||
|
const assert = require('node:assert/strict');
|
||||||
|
const { loadApplication } = require('./support/application.cjs');
|
||||||
|
function setup() {
|
||||||
|
const app = loadApplication('content');
|
||||||
|
const items = [['A', 'X'], ['A', 'Y'], ['B', 'Y'], ['C', 'Z'], ['A', 'X']].map(([customer, owner]) => ({
|
||||||
|
CustomerAccount: { PartyId: customer, PartyUniqueName: customer }, Owner: owner
|
||||||
|
}));
|
||||||
|
app.opportunitiesTableState.opportunitiesTableState.items = items;
|
||||||
|
app.opportunitiesFilters.resetCustomerFilter(items);
|
||||||
|
app.opportunitiesFilters.resetOwnerFilter(items);
|
||||||
|
return { filters: app.opportunitiesFilters, state: app.opportunitiesFilterState };
|
||||||
|
}
|
||||||
|
const labels = options => Array.from(options, option => option.label);
|
||||||
|
test('customers narrow owners without losing all-owner selection; widening restores options', () => {
|
||||||
|
const { filters, state } = setup();
|
||||||
|
state.selectedCustomerKeys = new Set(['party:A']);
|
||||||
|
assert.deepEqual(labels(filters.getAvailableOwnerOptions()), ['X', 'Y']);
|
||||||
|
assert.deepEqual(labels(filters.getAvailableCustomerOptions()), ['A', 'B', 'C']);
|
||||||
|
state.selectedCustomerKeys.add('party:C');
|
||||||
|
assert.deepEqual(labels(filters.getAvailableOwnerOptions()), ['X', 'Y', 'Z']);
|
||||||
|
assert.equal(state.selectedOwnerKeys.size, 3);
|
||||||
|
});
|
||||||
|
test('owners narrow customers and multi-selection combines their customers', () => {
|
||||||
|
const { filters, state } = setup();
|
||||||
|
state.selectedOwnerKeys = new Set(['name:Y']);
|
||||||
|
assert.deepEqual(labels(filters.getAvailableCustomerOptions()), ['A', 'B']);
|
||||||
|
assert.deepEqual(labels(filters.getAvailableOwnerOptions()), ['X', 'Y', 'Z']);
|
||||||
|
state.selectedOwnerKeys.add('name:Z');
|
||||||
|
assert.deepEqual(labels(filters.getAvailableCustomerOptions()), ['A', 'B', 'C']);
|
||||||
|
});
|
||||||
|
test('both filters use the opposite selection, independent of search and stage', () => {
|
||||||
|
const { filters, state } = setup();
|
||||||
|
state.selectedOwnerKeys = new Set(['name:Y']);
|
||||||
|
state.selectedCustomerKeys = new Set(['party:A']);
|
||||||
|
state.tableSearch = 'no match';
|
||||||
|
state.selectedStages.clear();
|
||||||
|
assert.deepEqual(labels(filters.getAvailableCustomerOptions()), ['A', 'B']);
|
||||||
|
assert.deepEqual(labels(filters.getAvailableOwnerOptions()), ['X', 'Y']);
|
||||||
|
state.selectedCustomerKeys.clear();
|
||||||
|
assert.deepEqual(labels(filters.getAvailableOwnerOptions()), []);
|
||||||
|
assert.deepEqual(labels(filters.getAvailableCustomerOptions()), ['A', 'B']);
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user