fix: recalculate date range from filter segments (#20633)
* fix: recalculate date range from filter segments * clean up * add unit tets for date-range utils --------- Co-authored-by: Sean Brydon <sean@Seans-Mac-mini.local>
This commit is contained in:
1 parent
6615b885d1
commit
0386723ae8
4 files changed
+197
-43
No files matched your search
@@ -25,6 +25,7 @@ import {
|
||||
PRESET_OPTIONS,
|
||||
getDefaultStartDate,
|
||||
getDefaultEndDate,
|
||||
getDateRangeFromPreset,
|
||||
type PresetOption,
|
||||
} from "../../lib/dateRange";
|
||||
import type { FilterableColumn, DateRangeFilterOptions } from "../../lib/types";
|
||||
@@ -36,44 +37,6 @@ type DateRangeFilterProps = {
|
||||
showClearButton?: boolean;
|
||||
};
|
||||
|
||||
const getDateRangeFromPreset = (val: string | null) => {
|
||||
let startDate;
|
||||
let endDate;
|
||||
const preset = PRESET_OPTIONS.find((o) => o.value === val);
|
||||
if (!preset) {
|
||||
return { startDate: getDefaultStartDate(), endDate: getDefaultEndDate(), preset: CUSTOM_PRESET };
|
||||
}
|
||||
|
||||
switch (val) {
|
||||
case "tdy": // Today
|
||||
startDate = dayjs().startOf("day");
|
||||
endDate = dayjs().endOf("day");
|
||||
break;
|
||||
case "w": // Last 7 days
|
||||
startDate = dayjs().subtract(1, "week").startOf("day");
|
||||
endDate = dayjs().endOf("day");
|
||||
break;
|
||||
case "t": // Last 30 days
|
||||
startDate = dayjs().subtract(30, "day").startOf("day");
|
||||
endDate = dayjs().endOf("day");
|
||||
break;
|
||||
case "m": // Month to Date
|
||||
startDate = dayjs().startOf("month");
|
||||
endDate = dayjs().endOf("day");
|
||||
break;
|
||||
case "y": // Year to Date
|
||||
startDate = dayjs().startOf("year");
|
||||
endDate = dayjs().endOf("day");
|
||||
break;
|
||||
default:
|
||||
startDate = getDefaultStartDate();
|
||||
endDate = getDefaultEndDate();
|
||||
break;
|
||||
}
|
||||
|
||||
return { startDate, endDate, preset };
|
||||
};
|
||||
|
||||
export const DateRangeFilter = ({ column, options, showClearButton = false }: DateRangeFilterProps) => {
|
||||
const filterValue = useFilterValue(column.id, ZDateRangeFilterValue);
|
||||
const { updateFilter, removeFilter } = useDataTable();
|
||||
@@ -137,11 +100,11 @@ export const DateRangeFilter = ({ column, options, showClearButton = false }: Da
|
||||
endDate,
|
||||
});
|
||||
} else {
|
||||
const r = getDateRangeFromPreset(val);
|
||||
const { preset, startDate, endDate } = getDateRangeFromPreset(val);
|
||||
updateValues({
|
||||
preset: r.preset,
|
||||
startDate: r.startDate,
|
||||
endDate: r.endDate,
|
||||
preset,
|
||||
startDate,
|
||||
endDate,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -4,7 +4,9 @@ import { useCallback, useMemo, useEffect } from "react";
|
||||
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
|
||||
import { recalculateDateRange } from "../lib/dateRange";
|
||||
import { ZSegmentStorage, type UseSegments } from "../lib/types";
|
||||
import { isDateRangeFilterValue } from "../lib/utils";
|
||||
|
||||
export const useSegments: UseSegments = ({
|
||||
tableIdentifier,
|
||||
@@ -25,9 +27,27 @@ export const useSegments: UseSegments = ({
|
||||
setPageIndex,
|
||||
setSearchTerm,
|
||||
}) => {
|
||||
const { data: segments, isFetching: isFetchingSegments } = trpc.viewer.filterSegments.list.useQuery({
|
||||
const { data: rawSegments, isFetching: isFetchingSegments } = trpc.viewer.filterSegments.list.useQuery({
|
||||
tableIdentifier,
|
||||
});
|
||||
|
||||
// Recalculate date ranges based on the current timestamp
|
||||
const segments = useMemo(() => {
|
||||
if (!rawSegments) return [];
|
||||
return rawSegments.map((segment) => ({
|
||||
...segment,
|
||||
activeFilters: segment.activeFilters.map((filter) => {
|
||||
if (isDateRangeFilterValue(filter.v)) {
|
||||
return {
|
||||
...filter,
|
||||
v: recalculateDateRange(filter.v),
|
||||
};
|
||||
}
|
||||
return filter;
|
||||
}),
|
||||
}));
|
||||
}, [rawSegments]);
|
||||
|
||||
const selectedSegment = useMemo(
|
||||
() => segments?.find((segment) => segment.id === segmentId),
|
||||
[segments, segmentId]
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import dayjs from "@calcom/dayjs";
|
||||
|
||||
import {
|
||||
getDateRangeFromPreset,
|
||||
recalculateDateRange,
|
||||
PRESET_OPTIONS,
|
||||
CUSTOM_PRESET,
|
||||
DEFAULT_PRESET,
|
||||
CUSTOM_PRESET_VALUE,
|
||||
} from "./dateRange";
|
||||
import { ColumnFilterType, type DateRangeFilterValue } from "./types";
|
||||
|
||||
// Mock dayjs to have consistent timestamps in tests
|
||||
vi.mock("@calcom/dayjs", () => {
|
||||
const mockDayjs = vi.fn(() => ({
|
||||
startOf: vi.fn().mockReturnThis(),
|
||||
endOf: vi.fn().mockReturnThis(),
|
||||
subtract: vi.fn().mockReturnThis(),
|
||||
toISOString: vi.fn().mockReturnValue("2024-03-20T00:00:00.000Z"),
|
||||
}));
|
||||
return {
|
||||
default: mockDayjs,
|
||||
};
|
||||
});
|
||||
|
||||
describe("getDateRangeFromPreset", () => {
|
||||
it("should return default dates for null value", () => {
|
||||
const result = getDateRangeFromPreset(null);
|
||||
expect(result.preset).toEqual(CUSTOM_PRESET);
|
||||
expect(dayjs).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should return today's date range for 'tdy' preset", () => {
|
||||
const result = getDateRangeFromPreset("tdy");
|
||||
expect(result.preset.value).toBe("tdy");
|
||||
expect(dayjs).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should return last 7 days range for 'w' preset", () => {
|
||||
const result = getDateRangeFromPreset("w");
|
||||
expect(result.preset.value).toBe("w");
|
||||
expect(dayjs).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should return last 30 days range for 't' preset", () => {
|
||||
const result = getDateRangeFromPreset("t");
|
||||
expect(result.preset.value).toBe("t");
|
||||
expect(dayjs).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should return month to date range for 'm' preset", () => {
|
||||
const result = getDateRangeFromPreset("m");
|
||||
expect(result.preset.value).toBe("m");
|
||||
expect(dayjs).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should return year to date range for 'y' preset", () => {
|
||||
const result = getDateRangeFromPreset("y");
|
||||
expect(result.preset.value).toBe("y");
|
||||
expect(dayjs).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("recalculateDateRange", () => {
|
||||
it("should return custom range as is", () => {
|
||||
const customFilter: DateRangeFilterValue = {
|
||||
type: ColumnFilterType.DATE_RANGE,
|
||||
data: {
|
||||
preset: CUSTOM_PRESET_VALUE,
|
||||
startDate: "2024-03-01T00:00:00.000Z",
|
||||
endDate: "2024-03-15T00:00:00.000Z",
|
||||
},
|
||||
};
|
||||
|
||||
const result = recalculateDateRange(customFilter);
|
||||
expect(result).toEqual(customFilter);
|
||||
});
|
||||
|
||||
it("should recalculate dates for non-custom presets", () => {
|
||||
const filter: DateRangeFilterValue = {
|
||||
type: ColumnFilterType.DATE_RANGE,
|
||||
data: {
|
||||
preset: "w",
|
||||
startDate: "2024-03-01T00:00:00.000Z",
|
||||
endDate: "2024-03-15T00:00:00.000Z",
|
||||
},
|
||||
};
|
||||
|
||||
const result = recalculateDateRange(filter);
|
||||
expect(result.type).toBe(ColumnFilterType.DATE_RANGE);
|
||||
expect(result.data.preset).toBe("w");
|
||||
expect(result.data.startDate).toBe("2024-03-20T00:00:00.000Z");
|
||||
expect(result.data.endDate).toBe("2024-03-20T00:00:00.000Z");
|
||||
});
|
||||
});
|
||||
|
||||
describe("PRESET_OPTIONS", () => {
|
||||
it("should contain all expected preset options", () => {
|
||||
const expectedValues = ["tdy", "w", "t", "m", "y", "c"];
|
||||
expect(PRESET_OPTIONS.map((o) => o.value)).toEqual(expectedValues);
|
||||
});
|
||||
|
||||
it("should have correct default preset", () => {
|
||||
expect(DEFAULT_PRESET.value).toBe("w");
|
||||
expect(DEFAULT_PRESET.i18nOptions).toEqual({ count: 7 });
|
||||
});
|
||||
|
||||
it("should have correct custom preset", () => {
|
||||
expect(CUSTOM_PRESET.value).toBe(CUSTOM_PRESET_VALUE);
|
||||
expect(CUSTOM_PRESET.labelKey).toBe("custom_range");
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,7 @@
|
||||
import dayjs from "@calcom/dayjs";
|
||||
|
||||
import { ColumnFilterType, type DateRangeFilterValue } from "./types";
|
||||
|
||||
export type PresetOptionValue = "c" | "w" | "m" | "y" | "t" | "tdy";
|
||||
|
||||
export type PresetOption = {
|
||||
@@ -29,3 +31,58 @@ export const PRESET_OPTIONS: PresetOption[] = [
|
||||
export const getDefaultStartDate = () => dayjs().subtract(1, "week").startOf("day");
|
||||
|
||||
export const getDefaultEndDate = () => dayjs().endOf("day");
|
||||
|
||||
export const getDateRangeFromPreset = (val: string | null) => {
|
||||
let startDate;
|
||||
let endDate;
|
||||
const preset = PRESET_OPTIONS.find((o) => o.value === val);
|
||||
if (!preset) {
|
||||
return { startDate: getDefaultStartDate(), endDate: getDefaultEndDate(), preset: CUSTOM_PRESET };
|
||||
}
|
||||
|
||||
switch (val) {
|
||||
case "tdy": // Today
|
||||
startDate = dayjs().startOf("day");
|
||||
endDate = dayjs().endOf("day");
|
||||
break;
|
||||
case "w": // Last 7 days
|
||||
startDate = dayjs().subtract(1, "week").startOf("day");
|
||||
endDate = dayjs().endOf("day");
|
||||
break;
|
||||
case "t": // Last 30 days
|
||||
startDate = dayjs().subtract(30, "day").startOf("day");
|
||||
endDate = dayjs().endOf("day");
|
||||
break;
|
||||
case "m": // Month to Date
|
||||
startDate = dayjs().startOf("month");
|
||||
endDate = dayjs().endOf("day");
|
||||
break;
|
||||
case "y": // Year to Date
|
||||
startDate = dayjs().startOf("year");
|
||||
endDate = dayjs().endOf("day");
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Invalid preset value: ${val}`);
|
||||
}
|
||||
|
||||
return { startDate, endDate, preset };
|
||||
};
|
||||
|
||||
export const recalculateDateRange = (filterValue: DateRangeFilterValue): DateRangeFilterValue => {
|
||||
// If it's a custom range, return as is
|
||||
if (filterValue.data.preset === CUSTOM_PRESET_VALUE) {
|
||||
return filterValue;
|
||||
}
|
||||
|
||||
// Recalculate dates based on the current timestamp
|
||||
const { startDate, endDate } = getDateRangeFromPreset(filterValue.data.preset);
|
||||
|
||||
return {
|
||||
type: ColumnFilterType.DATE_RANGE,
|
||||
data: {
|
||||
...filterValue.data,
|
||||
startDate: startDate.toISOString(),
|
||||
endDate: endDate.toISOString(),
|
||||
},
|
||||
};
|
||||
};
|
||||
Reference in new issue
Block a user