New filter panel layout at client page
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m20s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m20s
This commit is contained in:
@@ -125,25 +125,83 @@ const normalizeDateParam = (value) => {
|
||||
return `${yearValue}-${monthValue}-${dayValue}`;
|
||||
};
|
||||
|
||||
const buildDateFilter = ({ start, end } = {}) => {
|
||||
const normalizeTextFilter = (value) => {
|
||||
if (value === undefined || value === null) return '';
|
||||
return String(value).trim();
|
||||
};
|
||||
|
||||
const normalizeSellerFilter = (value) => {
|
||||
const normalizedValue = normalizeTextFilter(value);
|
||||
if (!normalizedValue) return null;
|
||||
|
||||
if (normalizedValue.startsWith('id:')) {
|
||||
const id = normalizeTextFilter(normalizedValue.slice(3));
|
||||
return id ? { type: 'id', value: id } : null;
|
||||
}
|
||||
|
||||
if (normalizedValue.startsWith('name:')) {
|
||||
const name = normalizeTextFilter(normalizedValue.slice(5));
|
||||
return name ? { type: 'name', value: name } : null;
|
||||
}
|
||||
|
||||
return { type: 'any', value: normalizedValue };
|
||||
};
|
||||
|
||||
const appendOrderMetadataFilters = (params, filters, range = {}) => {
|
||||
const marketplace = normalizeTextFilter(range.marketplace);
|
||||
const salesChannel = normalizeTextFilter(range.canal_venda || range.canalVenda);
|
||||
const seller = normalizeSellerFilter(range.seller || range.vendedor);
|
||||
|
||||
if (marketplace) {
|
||||
params.push(marketplace);
|
||||
filters.push(`NULLIF(TRIM(marketplace), '') = $${params.length}`);
|
||||
}
|
||||
|
||||
if (salesChannel) {
|
||||
params.push(salesChannel);
|
||||
filters.push(`NULLIF(TRIM(canal_venda), '') = $${params.length}`);
|
||||
}
|
||||
|
||||
if (seller?.type === 'id') {
|
||||
params.push(seller.value);
|
||||
filters.push(`NULLIF(TRIM(id_vendedor), '') = $${params.length}`);
|
||||
} else if (seller?.type === 'name') {
|
||||
params.push(seller.value);
|
||||
filters.push(`NULLIF(TRIM(nome_vendedor), '') = $${params.length}`);
|
||||
} else if (seller?.type === 'any') {
|
||||
params.push(seller.value);
|
||||
filters.push(`(NULLIF(TRIM(id_vendedor), '') = $${params.length} OR NULLIF(TRIM(nome_vendedor), '') = $${params.length})`);
|
||||
}
|
||||
};
|
||||
|
||||
const buildDateFilter = (range = {}) => {
|
||||
const { start, end } = range;
|
||||
const params = [];
|
||||
const filters = ['data_pedido_date IS NOT NULL'];
|
||||
const normalizedStart = normalizeDateParam(start);
|
||||
const normalizedEnd = normalizeDateParam(end);
|
||||
let startParamIndex = null;
|
||||
let endParamIndex = null;
|
||||
|
||||
if (normalizedStart) {
|
||||
params.push(normalizedStart);
|
||||
startParamIndex = params.length;
|
||||
filters.push(`data_pedido_date >= $${params.length}::date`);
|
||||
}
|
||||
|
||||
if (normalizedEnd) {
|
||||
params.push(normalizedEnd);
|
||||
endParamIndex = params.length;
|
||||
filters.push(`data_pedido_date <= $${params.length}::date`);
|
||||
}
|
||||
|
||||
appendOrderMetadataFilters(params, filters, range);
|
||||
|
||||
return {
|
||||
params,
|
||||
whereClause: `WHERE ${filters.join(' AND ')}`
|
||||
whereClause: `WHERE ${filters.join(' AND ')}`,
|
||||
startParamIndex,
|
||||
endParamIndex
|
||||
};
|
||||
};
|
||||
|
||||
@@ -576,6 +634,63 @@ const getClientAnalytics = async (range = {}) => {
|
||||
return clients;
|
||||
};
|
||||
|
||||
const getClientFilterOptions = async (range = {}) => {
|
||||
const dateRange = {
|
||||
start: range.start,
|
||||
end: range.end
|
||||
};
|
||||
const { params, whereClause } = buildDateFilter(dateRange);
|
||||
const [marketplaceResult, salesChannelResult, sellerResult] = await Promise.all([
|
||||
pool.query(`
|
||||
SELECT DISTINCT NULLIF(TRIM(marketplace), '') as value
|
||||
FROM orders
|
||||
${whereClause}
|
||||
AND NULLIF(TRIM(marketplace), '') IS NOT NULL
|
||||
ORDER BY value ASC;
|
||||
`, params),
|
||||
pool.query(`
|
||||
SELECT DISTINCT NULLIF(TRIM(canal_venda), '') as value
|
||||
FROM orders
|
||||
${whereClause}
|
||||
AND NULLIF(TRIM(canal_venda), '') IS NOT NULL
|
||||
ORDER BY value ASC;
|
||||
`, params),
|
||||
pool.query(`
|
||||
SELECT
|
||||
NULLIF(TRIM(id_vendedor), '') as id,
|
||||
NULLIF(TRIM(nome_vendedor), '') as name
|
||||
FROM orders
|
||||
${whereClause}
|
||||
AND (
|
||||
NULLIF(TRIM(id_vendedor), '') IS NOT NULL
|
||||
OR NULLIF(TRIM(nome_vendedor), '') IS NOT NULL
|
||||
)
|
||||
GROUP BY id, name
|
||||
ORDER BY COALESCE(name, id) ASC, id ASC;
|
||||
`, params)
|
||||
]);
|
||||
const sellerOptionsByValue = new Map();
|
||||
|
||||
sellerResult.rows.forEach(row => {
|
||||
const id = row.id || '';
|
||||
const name = row.name || '';
|
||||
const value = id ? `id:${id}` : `name:${name}`;
|
||||
if (!value || sellerOptionsByValue.has(value)) return;
|
||||
|
||||
sellerOptionsByValue.set(value, {
|
||||
value,
|
||||
id,
|
||||
name: name || id
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
marketplaces: marketplaceResult.rows.map(row => row.value).filter(Boolean),
|
||||
salesChannels: salesChannelResult.rows.map(row => row.value).filter(Boolean),
|
||||
sellers: [...sellerOptionsByValue.values()]
|
||||
};
|
||||
};
|
||||
|
||||
const getOrderGroupKey = (row) => (
|
||||
row.pedido_id ||
|
||||
`${row.data_pedido || getDateOnly(row.data_pedido_date) || ''}_${row.valor_pedido || 0}`
|
||||
@@ -720,14 +835,14 @@ const getClientDetailsAnalytics = async (clientToken, range = {}) => {
|
||||
};
|
||||
|
||||
const getRfmAnalytics = async (range = {}) => {
|
||||
const { params, whereClause } = buildDateFilter(range);
|
||||
const { params, whereClause, endParamIndex } = buildDateFilter(range);
|
||||
const normalizedStart = normalizeDateParam(range.start);
|
||||
const normalizedEnd = normalizeDateParam(range.end);
|
||||
const periodRecencyReferenceDate = normalizedEnd ? `$${params.length}::date` : 'CURRENT_DATE';
|
||||
const periodRecencyReferenceDate = normalizedEnd && endParamIndex ? `$${endParamIndex}::date` : 'CURRENT_DATE';
|
||||
const usePeriodAsHistory = !normalizedStart || normalizedStart <= '2000-01-01';
|
||||
|
||||
if (usePeriodAsHistory) {
|
||||
const clientRows = await getClientAnalytics({ end: normalizedEnd });
|
||||
const clientRows = await getClientAnalytics({ ...range, start: undefined, end: normalizedEnd });
|
||||
const recencyEnd = normalizedEnd || new Date().toISOString().slice(0, 10);
|
||||
const clients = buildRfmClients(clientRows.map(row => ({
|
||||
customerKey: row.customerKey,
|
||||
@@ -803,6 +918,11 @@ const getRfmAnalytics = async (range = {}) => {
|
||||
const recencyReferenceDate = normalizedEnd
|
||||
? `$${historyParams.push(normalizedEnd)}::date`
|
||||
: 'CURRENT_DATE';
|
||||
const historyFilters = [
|
||||
'data_pedido_date IS NOT NULL',
|
||||
`data_pedido_date <= ${recencyReferenceDate}`
|
||||
];
|
||||
appendOrderMetadataFilters(historyParams, historyFilters, range);
|
||||
const customerKeysParam = `$${historyParams.push(periodCustomerKeys)}::text[]`;
|
||||
|
||||
const historyResult = await client.query(`
|
||||
@@ -817,8 +937,7 @@ const getRfmAnalytics = async (range = {}) => {
|
||||
MAX(data_pedido_date) as last_purchase_date,
|
||||
GREATEST((${recencyReferenceDate} - MAX(data_pedido_date))::int, 0) as recency_days
|
||||
FROM orders
|
||||
WHERE data_pedido_date IS NOT NULL
|
||||
AND data_pedido_date <= ${recencyReferenceDate}
|
||||
WHERE ${historyFilters.join(' AND ')}
|
||||
GROUP BY customer_key
|
||||
),
|
||||
scored_history AS MATERIALIZED (
|
||||
@@ -923,6 +1042,7 @@ module.exports = {
|
||||
isClientToken,
|
||||
getFrequencyScore,
|
||||
getClientDetailsAnalytics,
|
||||
getClientFilterOptions,
|
||||
getPreviousDate,
|
||||
getRecencyScore,
|
||||
getRfmAnalytics,
|
||||
|
||||
Reference in New Issue
Block a user