@rsc-labs/medusa-store-analytics
Version:
Get analytics data about your store
189 lines (181 loc) • 12.1 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ChartCurrentPrevious = exports.ChartCustomTooltip = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
/*
* Copyright 2024 RSC-Labs, https://rsoftcon.com/
*
* MIT License
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const ui_1 = require("@medusajs/ui");
const chartUtils_1 = require("./utils/chartUtils");
const recharts_1 = require("recharts");
const react_1 = require("react");
const material_1 = require("@mui/material");
const incrementDate = (date, resolutionType) => {
switch (resolutionType) {
case chartUtils_1.ChartResolutionType.DayMonth:
date.setDate(date.getDate() + 1);
break;
case chartUtils_1.ChartResolutionType.Month:
date.setMonth(date.getMonth() + 1);
break;
default:
date.setDate(date.getDate() + 1);
}
};
const generateChartData = (data, fromDate, toDate, chartResolutionType, toCompareDate, connectEmptyPointsUsingPreviousValue) => {
const currentData = data.current;
const previousData = data.previous;
const startFromDate = new Date(fromDate);
const offsetTime = toDate.getTime() - (toCompareDate ? toCompareDate.getTime() : fromDate.getTime());
const dataPoints = [];
let currentDataValue;
let previousDataValue;
while (startFromDate.getTime() < toDate.getTime() || (0, chartUtils_1.compareDatesBasedOnResolutionType)(startFromDate, toDate, chartResolutionType)) {
const currentOrder = currentData.find(order => (0, chartUtils_1.compareDatesBasedOnResolutionType)(new Date(order.date), startFromDate, chartResolutionType));
const offsetDate = new Date(startFromDate);
offsetDate.setTime(offsetDate.getTime() - offsetTime);
const previousOrder = previousData.find(previous => (0, chartUtils_1.compareDatesBasedOnResolutionType)(new Date(previous.date), offsetDate, chartResolutionType));
if (connectEmptyPointsUsingPreviousValue) {
if (currentOrder) {
currentDataValue = parseInt(currentOrder.value);
}
if (previousOrder) {
previousDataValue = parseInt(previousOrder.value);
}
dataPoints.push({
current: {
date: new Date(startFromDate),
value: currentOrder ? parseInt(currentOrder.value) : (currentDataValue ? currentDataValue : undefined),
},
previous: {
date: new Date(offsetDate),
value: previousOrder ? parseInt(previousOrder.value) : (previousDataValue ? previousDataValue : undefined),
}
});
}
else {
dataPoints.push({
current: {
date: new Date(startFromDate),
value: currentOrder ? parseInt(currentOrder.value) : 0
},
previous: {
date: new Date(offsetDate),
value: previousOrder ? parseInt(previousOrder.value) : 0,
}
});
}
incrementDate(startFromDate, chartResolutionType);
}
if (connectEmptyPointsUsingPreviousValue) {
for (let i = dataPoints.length - 1; i >= 0; i--) {
if (dataPoints[i].current.value === undefined) {
if (dataPoints[dataPoints.length - 1].previous.value) {
dataPoints[i].current.value = dataPoints[dataPoints.length - 1].previous.value;
}
else {
dataPoints[i].current.value = 0;
}
}
if (dataPoints[i].previous.value) {
previousDataValue = dataPoints[i].previous.value;
}
else {
dataPoints[i].previous.value = previousDataValue;
}
}
}
return dataPoints;
};
const ChartCustomTooltip = ({ active, payload, label, resolutionType }) => {
if (active && payload && payload.length) {
switch (resolutionType) {
case chartUtils_1.ChartResolutionType.DayMonth:
return ((0, jsx_runtime_1.jsxs)(ui_1.Container, { children: [(0, jsx_runtime_1.jsxs)(ui_1.Heading, { level: "h3", style: { color: payload[0].color }, children: [`${(0, chartUtils_1.getChartTooltipDate)(payload[0].payload.current.date, resolutionType)}`, " : ", payload[0].payload.current.value] }), payload[1] !== undefined &&
(0, jsx_runtime_1.jsxs)(ui_1.Heading, { level: "h3", style: { color: payload[1].color }, children: [`${(0, chartUtils_1.getChartTooltipDate)(payload[1].payload.previous.date, resolutionType)}`, " : ", payload[1].payload.previous.value] })] }));
case chartUtils_1.ChartResolutionType.Month:
return ((0, jsx_runtime_1.jsxs)(ui_1.Container, { children: [(0, jsx_runtime_1.jsxs)(ui_1.Heading, { level: "h3", style: { color: payload[0].color }, children: [`${(0, chartUtils_1.getChartTooltipDate)(payload[0].payload.current.date, resolutionType)}`, " : ", payload[0].payload.current.value] }), payload[1] !== undefined &&
(0, jsx_runtime_1.jsxs)(ui_1.Heading, { level: "h3", style: { color: payload[1].color }, children: [`${(0, chartUtils_1.getChartTooltipDate)(payload[1].payload.previous.date, resolutionType)}`, " : ", payload[1].payload.previous.value] })] }));
}
}
return null;
};
exports.ChartCustomTooltip = ChartCustomTooltip;
/*
toDate is inclusive. It means that:
fromDate: "2024-04-24"
toDate: "2024-04-30"
Analytics shall include `toDate` so it takes 7 days (including 2024-04-30)
fromCompareDate: "2024-04-17"
toCompareDate: "2024-04-24"
Analytics shall compare to 7 days excluding 2024-04-24 (e.g. 2024-04-30 is compared to 2024-04-23, not 2024-04-24).
toDate is inclusive to cover "today" date - so we need to cover situation when someone wants to see everything until now.
We cannot use 2024-05-01 because then it is taken as day to show, while we want to show maximum 2024-04-30.
toCompareDate is exclusive because backend is using fetches like created_at < toCompareDate, so it does not cover data at toCompareDate
Comparison then we will have following algorithm:
1) Take "toDate", remove "time" part and add whole day.
2) Take times in milis from every date and compare.
*/
const areRangesTheSame = (fromDate, toDate, fromCompareDate, toCompareDate) => {
function isToday(date) {
const today = new Date();
today.setHours(0, 0, 0, 0);
const givenDate = new Date(date);
givenDate.setHours(0, 0, 0, 0);
return today.getTime() === givenDate.getTime();
}
if (fromCompareDate) {
const oneDay = 24 * 60 * 60 * 1000;
if (toCompareDate) {
// Cover situation when toDate is today so gives jsut couple of hours while we need the whole day.
if (isToday(toDate)) {
const diffBase = Math.ceil(Math.abs((toDate.getTime() - fromDate.getTime()) / oneDay));
const diffCompare = Math.round(Math.abs((toCompareDate.getTime() - fromCompareDate.getTime()) / oneDay));
return (diffBase == diffCompare);
}
const diffBase = Math.round(Math.abs((toDate.getTime() - fromDate.getTime()) / oneDay));
const diffCompare = Math.round(Math.abs((toCompareDate.getTime() - fromCompareDate.getTime()) / oneDay));
return (diffBase == diffCompare);
}
const diffBase = Math.ceil(Math.abs((toDate.getTime() - fromDate.getTime()) / oneDay));
const diffCompare = Math.ceil(Math.abs((Date.now() - fromCompareDate.getTime()) / oneDay));
return (diffBase == diffCompare);
}
return true;
};
const ChartCurrentPrevious = ({ rawChartData, fromDate, toDate, fromCompareDate, toCompareDate, compareEnabled, connectEmptyPointsUsingPreviousValue }) => {
const [chartDataPoints, setChartData] = (0, react_1.useState)([]);
const resolutionType = (0, chartUtils_1.calculateResolution)(fromDate, toDate);
(0, react_1.useEffect)(() => {
const chartDataPoints = generateChartData(rawChartData, fromDate, toDate, resolutionType, toCompareDate, connectEmptyPointsUsingPreviousValue);
setChartData(chartDataPoints);
}, [rawChartData, fromDate, toDate]);
if (!areRangesTheSame(fromDate, toDate, fromCompareDate, toCompareDate)) {
const currentPeriodInDays = Math.ceil((toDate.getTime() - fromDate.getTime()) / (24 * 60 * 60 * 1000));
let precedingPeriodInDays = 0;
if (fromCompareDate) {
if (toCompareDate) {
precedingPeriodInDays = Math.ceil((toCompareDate.getTime() - fromCompareDate.getTime()) / (24 * 60 * 60 * 1000));
}
else {
precedingPeriodInDays = Math.ceil((new Date(Date.now()).getTime() - fromCompareDate.getTime()) / (24 * 60 * 60 * 1000));
}
}
return ((0, jsx_runtime_1.jsx)(material_1.Box, { width: 500, height: 400, display: "flex", alignItems: "center", justifyContent: "center", children: (0, jsx_runtime_1.jsxs)(material_1.Grid, { container: true, direction: 'column', justifyContent: 'center', alignItems: 'center', children: [(0, jsx_runtime_1.jsx)(material_1.Grid, { item: true, children: (0, jsx_runtime_1.jsx)(ui_1.Text, { children: "Chart can be shown only for the same length of ranges." }) }), (0, jsx_runtime_1.jsx)(material_1.Grid, { item: true, children: (0, jsx_runtime_1.jsx)(ui_1.Text, { children: `You are comparing ${currentPeriodInDays} days to ${precedingPeriodInDays} days` }) })] }) }));
}
return ((0, jsx_runtime_1.jsxs)(recharts_1.AreaChart, { width: 500, height: 400, data: chartDataPoints, margin: {
top: 20,
right: 0,
left: 0,
bottom: 0,
}, children: [(0, jsx_runtime_1.jsx)(recharts_1.CartesianGrid, { strokeDasharray: "3 3" }), (0, jsx_runtime_1.jsxs)("defs", { children: [(0, jsx_runtime_1.jsxs)("linearGradient", { id: "colorPrevious", x1: "0", y1: "0", x2: "0", y2: "1", children: [(0, jsx_runtime_1.jsx)("stop", { offset: "5%", stopColor: "#8884d8", stopOpacity: 0.8 }), (0, jsx_runtime_1.jsx)("stop", { offset: "95%", stopColor: "#8884d8", stopOpacity: 0 })] }), (0, jsx_runtime_1.jsxs)("linearGradient", { id: "colorCurrent", x1: "0", y1: "0", x2: "0", y2: "1", children: [(0, jsx_runtime_1.jsx)("stop", { offset: "5%", stopColor: "#82ca9d", stopOpacity: 0.8 }), (0, jsx_runtime_1.jsx)("stop", { offset: "95%", stopColor: "#82ca9d", stopOpacity: 0 })] })] }), (0, jsx_runtime_1.jsx)(recharts_1.XAxis, { dataKey: (value) => (0, chartUtils_1.getChartDateName)(value.current.date, resolutionType, fromDate, toDate), minTickGap: 15, interval: 'preserveStartEnd' }), (0, jsx_runtime_1.jsx)(recharts_1.YAxis, {}), (0, jsx_runtime_1.jsx)(recharts_1.Tooltip, { content: (0, jsx_runtime_1.jsx)(exports.ChartCustomTooltip, { active: false, payload: [], label: "", resolutionType: resolutionType }) }), (0, jsx_runtime_1.jsx)(recharts_1.Area, { name: (compareEnabled && fromCompareDate) ? (0, chartUtils_1.getLegendName)(true) : undefined, type: "monotone", dataKey: "current.value", stroke: "#82ca9d", fillOpacity: 1, fill: "url(#colorCurrent)" }), (compareEnabled && fromCompareDate) && (0, jsx_runtime_1.jsx)(recharts_1.Area, { name: (0, chartUtils_1.getLegendName)(false), type: "monotone", dataKey: "previous.value", stroke: "#8884d8", fillOpacity: 1, fill: "url(#colorPrevious)" }), (compareEnabled && fromCompareDate) && (0, jsx_runtime_1.jsx)(recharts_1.Legend, { verticalAlign: "bottom", height: 36, iconType: "circle" })] }));
};
exports.ChartCurrentPrevious = ChartCurrentPrevious;