Skip to content

Commit 38fa0fa

Browse files
fix: insights UI/UX (#16610)
* remove old code remnant * - * WIP * type change * -- * typefix * fix type * fix type * change type name * add basewhere to eventsByStatus * add basewhere to the rest * fix rendering null popular event list item * URL params for start and endtime to have YYYY-MM-DD format * remove console log * resolve suggestion --------- Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
1 parent 2872b26 commit 38fa0fa

File tree

10 files changed

+437
-797
lines changed

10 files changed

+437
-797
lines changed

apps/web/public/static/locales/en/common.json

+4
Original file line numberDiff line numberDiff line change
@@ -2618,5 +2618,9 @@
26182618
"you_are_unauthorized_to_make_this_change_to_the_booking": "You are unauthorized to make this change to the booking",
26192619
"hide_calendar_event_details": "Hide calendar event details on shared calendars",
26202620
"description_hide_calendar_event_details": "When a calendar is shared, events are visible to readers but their details are hidden from those without write access.",
2621+
"last_number_of_days": "last {{count}} days",
2622+
"month_to_date": "month to date",
2623+
"year_to_date": "year to date",
2624+
"custom_range": "custom range",
26212625
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
26222626
}

packages/features/insights/components/AverageEventDurationChart.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@ export const AverageEventDurationChart = () => {
1414
const { filter } = useFilterContext();
1515
const { dateRange, selectedMemberUserId, isAll, initialConfig } = filter;
1616
const [startDate, endDate] = dateRange;
17-
const { selectedTeamId: teamId, selectedUserId } = filter;
17+
const { selectedTeamId: teamId, selectedUserId, selectedEventTypeId } = filter;
1818
const initialConfigIsReady = !!(initialConfig?.teamId || initialConfig?.userId || initialConfig?.isAll);
1919
const { data, isSuccess, isPending } = trpc.viewer.insights.averageEventDuration.useQuery(
2020
{
2121
startDate: startDate.toISOString(),
2222
endDate: endDate.toISOString(),
23-
teamId: teamId ?? undefined,
23+
teamId,
24+
eventTypeId: selectedEventTypeId ?? undefined,
2425
memberUserId: selectedMemberUserId ?? undefined,
2526
userId: selectedUserId ?? undefined,
2627
isAll,

packages/features/insights/components/BookingKPICards.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { KPICard } from "./KPICard";
1111
export const BookingKPICards = () => {
1212
const { t } = useLocale();
1313
const { filter } = useFilterContext();
14+
1415
const { dateRange, selectedEventTypeId, selectedUserId, selectedMemberUserId, isAll, initialConfig } =
1516
filter;
1617
const initialConfigIsReady = !!(initialConfig?.teamId || initialConfig?.userId || initialConfig?.isAll);

packages/features/insights/components/PopularEventsTable.tsx

+16-11
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import { LoadingInsight } from "./LoadingInsights";
1010
export const PopularEventsTable = () => {
1111
const { t } = useLocale();
1212
const { filter } = useFilterContext();
13-
const { dateRange, selectedMemberUserId, selectedUserId, isAll, initialConfig } = filter;
13+
const { dateRange, selectedMemberUserId, selectedUserId, isAll, initialConfig, selectedEventTypeId } =
14+
filter;
1415
const [startDate, endDate] = dateRange;
1516
const { selectedTeamId: teamId } = filter;
1617

@@ -20,6 +21,7 @@ export const PopularEventsTable = () => {
2021
endDate: endDate.toISOString(),
2122
teamId: teamId ?? undefined,
2223
userId: selectedUserId ?? undefined,
24+
eventTypeId: selectedEventTypeId ?? undefined,
2325
memberUserId: selectedMemberUserId ?? undefined,
2426
isAll,
2527
},
@@ -41,16 +43,19 @@ export const PopularEventsTable = () => {
4143
<Title className="text-emphasis">{t("popular_events")}</Title>
4244
<Table className="mt-5">
4345
<TableBody>
44-
{data.map((item) => (
45-
<TableRow key={item.eventTypeId}>
46-
<TableCell className="text-default">{item.eventTypeName}</TableCell>
47-
<TableCell>
48-
<Text className="text-default text-right">
49-
<strong>{item.count}</strong>
50-
</Text>
51-
</TableCell>
52-
</TableRow>
53-
))}
46+
{data.map(
47+
(item) =>
48+
item.eventTypeId && (
49+
<TableRow key={item.eventTypeId}>
50+
<TableCell className="text-default">{item.eventTypeName}</TableCell>
51+
<TableCell>
52+
<Text className="text-default text-right">
53+
<strong>{item.count}</strong>
54+
</Text>
55+
</TableCell>
56+
</TableRow>
57+
)
58+
)}
5459
</TableBody>
5560
</Table>
5661
{data.length === 0 && (

packages/features/insights/context/FiltersProvider.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export function FiltersProvider({ children }: { children: React.ReactNode }) {
6060
dateRange: [
6161
startTimeParsed ? dayjs(startTimeParsed) : dayjs().subtract(1, "week"),
6262
endTimeParsed ? dayjs(endTimeParsed) : dayjs(),
63-
"w",
63+
!startTimeParsed && !endTimeParsed ? "w" : null,
6464
],
6565
selectedTimeView: "week",
6666
selectedUserId: userIdParsed || null,
@@ -131,8 +131,8 @@ export function FiltersProvider({ children }: { children: React.ReactNode }) {
131131
setParamsIfDefined("userId", selectedUserId || initialConfig?.userId);
132132
setParamsIfDefined("eventTypeId", selectedEventTypeId);
133133
setParamsIfDefined("isAll", isAll || initialConfig?.isAll);
134-
setParamsIfDefined("startTime", startTime?.toISOString());
135-
setParamsIfDefined("endTime", endTime?.toISOString());
134+
setParamsIfDefined("startTime", startTime?.format("YYYY-MM-DD"));
135+
setParamsIfDefined("endTime", endTime?.format("YYYY-MM-DD"));
136136
setParamsIfDefined("filter", selectedFilter?.[0]);
137137
router.push(`${pathname}?${newSearchParams.toString()}`);
138138
},

packages/features/insights/filters/DateSelect.tsx

+80-46
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,102 @@
1-
import { DateRangePicker } from "@tremor/react";
1+
import { useState } from "react";
22

33
import dayjs from "@calcom/dayjs";
44
import { useLocale } from "@calcom/lib/hooks/useLocale";
5+
import { DateRangePicker } from "@calcom/ui";
6+
import { Select } from "@calcom/ui";
57

68
import { useFilterContext } from "../context/provider";
79
import "./DateSelect.css";
810

9-
type RangeType = "tdy" | "w" | "t" | "m" | "y" | undefined | null;
10-
1111
export const DateSelect = () => {
1212
const { t } = useLocale();
13+
const presetOptions = [
14+
{ label: t("today"), value: "tdy" },
15+
{ label: t("last_number_of_days", { count: 7 }), value: "w" },
16+
{ label: t("last_number_of_days", { count: 30 }), value: "t" },
17+
{ label: t("month_to_date"), value: "m" },
18+
{ label: t("year_to_date"), value: "y" },
19+
{ label: t("custom_range"), value: null },
20+
];
21+
1322
const { filter, setConfigFilters } = useFilterContext();
1423
const currentDate = dayjs();
15-
const [startDate, endDate, range] = filter?.dateRange || [null, null, null];
24+
const [initialStartDate, initialEndDate, range] = filter?.dateRange || [null, null, null];
25+
const [startDate, setStartDate] = useState(initialStartDate);
26+
const [endDate, setEndDate] = useState(initialEndDate);
1627
const startValue = startDate?.toDate() || null;
1728
const endValue = endDate?.toDate() || null;
18-
return (
19-
<div className="custom-date max-w-96 w-full sm:w-auto">
20-
<DateRangePicker
21-
value={[startValue, endValue, range]}
22-
defaultValue={[startValue, endValue, range]}
23-
onValueChange={(datesArray) => {
24-
const [selected, ...rest] = datesArray;
25-
const [start, end, range] = datesArray;
26-
// If range has value and it's of type RangeType
27-
28-
if (
29-
range &&
30-
(range === "tdy" || range === "w" || range === "t" || range === "m" || range === "y")
31-
) {
32-
setConfigFilters({
33-
dateRange: [dayjs(start).startOf("d"), dayjs(end).endOf("d"), range],
34-
});
29+
const [selectedPreset, setSelectedPreset] = useState(presetOptions.find((c) => c.value === range));
3530

36-
return;
37-
} else if (start && !end) {
38-
// If only start time has value that means selected date should push to dateRange with last value null
39-
const currentDates = filter.dateRange;
40-
if (currentDates && currentDates.length > 0) {
41-
// remove last position of array
42-
currentDates.pop();
43-
// push new value to array
44-
currentDates.push(dayjs(selected));
45-
// if lenght > 2 then remove first value
46-
if (currentDates.length > 2) {
47-
currentDates.shift();
48-
}
49-
setConfigFilters({
50-
dateRange: [currentDates[0], currentDates[1], null],
51-
});
52-
}
31+
const updateDateRange = (val: string | null) => {
32+
setSelectedPreset(presetOptions.find((c) => c.value === val));
33+
let startDate = dayjs();
34+
let endDate = dayjs();
5335

54-
return;
55-
}
36+
switch (val) {
37+
case "tdy": // Today
38+
startDate = dayjs().startOf("day");
39+
endDate = dayjs().endOf("day");
40+
break;
41+
case "w": // Last 7 days
42+
startDate = dayjs().subtract(7, "day").startOf("day");
43+
endDate = dayjs().endOf("day");
44+
break;
45+
case "t": // Last 30 days
46+
startDate = dayjs().subtract(30, "day").startOf("day");
47+
endDate = dayjs().endOf("day");
48+
break;
49+
case "m": // Month to Date
50+
startDate = dayjs().startOf("month");
51+
endDate = dayjs().endOf("day");
52+
break;
53+
case "y": // Year to Date
54+
startDate = dayjs().startOf("year");
55+
endDate = dayjs().endOf("day");
56+
break;
57+
default:
58+
break;
59+
}
60+
// Update the datepicker date selection
61+
setStartDate(startDate);
62+
setEndDate(endDate);
63+
// Update the configuration filter
64+
setConfigFilters({
65+
dateRange: [dayjs(startDate), dayjs(endDate), val],
66+
});
67+
};
5668

57-
// If range has value and it's of type RangeType
69+
return (
70+
<div className="ml me-2 ms-2 inline-flex space-x-2 rtl:space-x-reverse">
71+
<DateRangePicker
72+
dates={{
73+
startDate: startValue,
74+
endDate: endValue,
5875
}}
59-
options={undefined}
60-
enableDropdown={true}
61-
placeholder={t("select_date_range")}
62-
enableYearPagination={true}
6376
minDate={currentDate.subtract(2, "year").toDate()}
6477
maxDate={currentDate.toDate()}
65-
color="gray"
78+
disabled={false}
79+
onDatesChange={({ startDate, endDate }) => {
80+
setConfigFilters({
81+
dateRange: [dayjs(startDate), dayjs(endDate), null],
82+
});
83+
setStartDate(dayjs(startDate));
84+
setEndDate(dayjs(endDate));
85+
setSelectedPreset(presetOptions.find((c) => c.value === null));
86+
}}
87+
/>
88+
<Select
89+
variant="default"
90+
data-testid="insights-preset"
91+
options={presetOptions}
92+
value={selectedPreset}
93+
className="w-40 capitalize text-black"
94+
defaultValue={selectedPreset}
95+
onChange={(e) => {
96+
if (e) {
97+
updateDateRange(e.value);
98+
}
99+
}}
66100
/>
67101
</div>
68102
);

packages/features/insights/filters/index.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export const Filters = () => {
7272
/>
7373
</Tooltip>
7474
</ButtonGroup> */}
75-
<div className="flex flex-col-reverse gap-2 sm:flex-row sm:flex-nowrap sm:justify-between">
75+
<div className="flex flex-col-reverse sm:flex-row sm:flex-nowrap sm:justify-between">
7676
<Download />
7777
<DateSelect />
7878
</div>

0 commit comments

Comments
 (0)