From 6c51e2a970395110605077ee5e7a209c12bf3fe7 Mon Sep 17 00:00:00 2001 From: alannnc Date: Tue, 28 Mar 2023 16:24:57 -0700 Subject: [PATCH] insights follow up (#7922) * init page * init insights frontend * nit * nit * nit * merge * fixed icons * i18n, needs features * Init insights trpc * Using trpc on client * Added events timeline * Seed analytics script * connect ui with trpc * Added and fixed event time lines * WIP popular days and avg time duration event type * added new metric graphs * improved upgrade tip * always show upgrade screen * upgrade tremor.so and select inputs for page * Remove log * Move everything to components and add context * Fix select types using calcom ui one * Adding translations * Add missing translations * Add more translations * min fix * Fixes for date select * Prefer early return and mobile design fixes * Fix style for mobile * Fix data with userId filter from popular events * add user id to average time duration * fix types for select-react * Removed submodules * Delete website * Update yarn.lock * Code organization and type fixes * trpc fixes * Builds are now passing * Relocates server code * Add url state in insights * Update FiltersProvider.tsx * Cleanup * Update embed-iframe.ts * Update FilterType.tsx * Update seed-app-store.config.json * Update index.tsx * Renamed seeder * Update FiltersProvider.tsx * Fix for query params * no wrap on lg screen * Fix shadow borders from tremor components, fix title font * Add ring-gray to match filters * add cursor pointer * copy improvements * blue to black * fixed date select focus * Adds missing translation strings * Fix url state for filter type * Apply suggestions from code review * Updated yarn lock * Adds feature flag * Type fix --------- Co-authored-by: Peer Richelsen Co-authored-by: Peer Richelsen Co-authored-by: zomars --- apps/web/pages/404.tsx | 39 +++++- apps/web/pages/insights/index.tsx | 23 +++- apps/web/public/static/locales/en/common.json | 9 +- apps/web/public/static/locales/es/common.json | 1 + apps/web/styles/globals.css | 2 + packages/features/flags/config.ts | 3 +- .../components/AverageEventDurationChart.tsx | 7 +- .../components/BookingStatusLineChart.tsx | 7 +- .../features/insights/components/Card.tsx | 17 +++ .../features/insights/components/KPICard.tsx | 11 +- .../LeastBookedTeamMembersTable.tsx | 11 +- .../components/MostBookedTeamMembersTable.tsx | 7 +- .../components/PopularEventsTable.tsx | 32 ++--- .../insights/context/FiltersProvider.tsx | 112 ++++++++++++++++-- .../features/insights/filters/DateSelect.css | 7 ++ .../features/insights/filters/DateSelect.tsx | 78 ++++++------ .../features/insights/filters/FilterType.tsx | 22 ++-- packages/features/insights/filters/index.tsx | 35 +++++- .../features/insights/server/trpc-router.ts | 7 +- packages/features/shell/Shell.tsx | 2 +- .../migration.sql | 10 ++ packages/prisma/package.json | 2 +- .../{seed-analytics.ts => seed-insights.ts} | 0 yarn.lock | 23 +--- 24 files changed, 338 insertions(+), 129 deletions(-) create mode 100644 packages/features/insights/components/Card.tsx create mode 100644 packages/features/insights/filters/DateSelect.css create mode 100644 packages/prisma/migrations/20230329000000_add_insights_feature_flag/migration.sql rename packages/prisma/{seed-analytics.ts => seed-insights.ts} (100%) diff --git a/apps/web/pages/404.tsx b/apps/web/pages/404.tsx index 7d7abeef0e..4447be19d4 100644 --- a/apps/web/pages/404.tsx +++ b/apps/web/pages/404.tsx @@ -6,7 +6,7 @@ import { useEffect, useState } from "react"; import { COMPANY_NAME, DEVELOPER_DOCS, DOCS_URL, JOIN_SLACK, WEBSITE_URL } from "@calcom/lib/constants"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { HeadSeo } from "@calcom/ui"; -import { FiFileText, FiCheck, FiBookOpen, FiChevronRight } from "@calcom/ui/components/icon"; +import { FiBookOpen, FiCheck, FiChevronRight, FiFileText } from "@calcom/ui/components/icon"; import { ssgInit } from "@server/lib/ssg"; @@ -40,6 +40,43 @@ export default function Custom404() { const isSubpage = router.asPath.includes("/", 2) || isSuccessPage; const isSignup = router.asPath.startsWith("/signup"); const isCalcom = process.env.NEXT_PUBLIC_WEBAPP_URL === "https://app.cal.com"; + /** + * If we're on 404 and the route is insights it means it is disabled + * TODO: Abstract this for all disabled features + **/ + const isInsights = router.asPath.startsWith("/insights"); + if (isInsights) { + return ( + <> + +
+
+
+

{t("error_404")}

+

+ Feature is currently disabled +

+
+
+
+ + {t("or_go_back_home")} + + +
+
+
+
+ + ); + } return ( <> diff --git a/apps/web/pages/insights/index.tsx b/apps/web/pages/insights/index.tsx index 828bcb286f..30877fc64a 100644 --- a/apps/web/pages/insights/index.tsx +++ b/apps/web/pages/insights/index.tsx @@ -1,3 +1,4 @@ +import { getFeatureFlagMap } from "@calcom/features/flags/server/utils"; import { AverageEventDurationChart, BookingKPICards, @@ -39,17 +40,17 @@ export default function InsightsPage() { const { data: user } = trpc.viewer.me.useQuery(); const features = [ { - icon: , + icon: , title: t("view_bookings_across"), description: t("view_bookings_across_description"), }, { - icon: , + icon: , title: t("identify_booking_trends"), description: t("identify_booking_trends_description"), }, { - icon: , + icon: , title: t("spot_popular_event_types"), description: t("spot_popular_event_types_description"), }, @@ -114,3 +115,19 @@ export default function InsightsPage() { ); } + +// If feature flag is disabled, return not found on getServerSideProps +export const getServerSideProps = async () => { + const prisma = await import("@calcom/prisma").then((mod) => mod.default); + const flags = await getFeatureFlagMap(prisma); + + if (flags.insights === false) { + return { + notFound: true, + }; + } + + return { + props: {}, + }; +}; diff --git a/apps/web/public/static/locales/en/common.json b/apps/web/public/static/locales/en/common.json index 605641182d..61a5918634 100644 --- a/apps/web/public/static/locales/en/common.json +++ b/apps/web/public/static/locales/en/common.json @@ -1218,6 +1218,7 @@ "impersonation": "Impersonation", "impersonation_description": "Settings to manage user impersonation", "users": "Users", + "user": "User", "profile_description": "Manage settings for your {{appName}} profile", "users_description": "Here you can find a list of all users", "users_listing": "User listing", @@ -1687,7 +1688,9 @@ "events_rescheduled": "Events Rescheduled", "from_last_period": "from last period", "from_to_date_period": "From: {{startDate}} To: {{endDate}}", - "analytics_for_organisation": "Analytics for {{organisationName}}", - "subtitle_analytics": "This is a organisation analytics", - "event_trends": "Event Trends" + "analytics_for_organisation": "Insights for {{organisationName}}", + "subtitle_analytics": "Learn more about your team's activity", + "event_trends": "Event Trends", + "clear_filters": "Clear Filters", + "insights": "Insights" } diff --git a/apps/web/public/static/locales/es/common.json b/apps/web/public/static/locales/es/common.json index fe96af4870..30c34677b4 100644 --- a/apps/web/public/static/locales/es/common.json +++ b/apps/web/public/static/locales/es/common.json @@ -1213,6 +1213,7 @@ "impersonation": "Suplantación", "impersonation_description": "Configuración para administrar la suplantación del usuario", "users": "Usuarios", + "user": "Usuario", "profile_description": "Administra los ajustes para tu perfil de {{appName}}", "users_description": "Aquí puede encontrar una lista de todos los usuarios", "users_listing": "Lista de usuarios", diff --git a/apps/web/styles/globals.css b/apps/web/styles/globals.css index 870aefe1f8..662a606ee5 100644 --- a/apps/web/styles/globals.css +++ b/apps/web/styles/globals.css @@ -497,3 +497,5 @@ hr { ::-webkit-search-cancel-button { -webkit-appearance: none; } + + diff --git a/packages/features/flags/config.ts b/packages/features/flags/config.ts index 9292a6dc0c..dc96c8b9ee 100644 --- a/packages/features/flags/config.ts +++ b/packages/features/flags/config.ts @@ -4,8 +4,9 @@ **/ export type AppFlags = { emails: boolean; + insights: boolean; teams: boolean; webhooks: boolean; workflows: boolean; - "booking-page-v2": boolean; + "v2-booking-page": boolean; }; diff --git a/packages/features/insights/components/AverageEventDurationChart.tsx b/packages/features/insights/components/AverageEventDurationChart.tsx index 1fa255b3e0..e01f75caf9 100644 --- a/packages/features/insights/components/AverageEventDurationChart.tsx +++ b/packages/features/insights/components/AverageEventDurationChart.tsx @@ -1,10 +1,11 @@ -import { Card, LineChart, Title } from "@tremor/react"; +import { LineChart, Title } from "@tremor/react"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { trpc } from "@calcom/trpc"; import { useFilterContext } from "../context/provider"; import { valueFormatter } from "../lib/valueFormatter"; +import { CardInsights } from "./Card"; export const AverageEventDurationChart = () => { const { t } = useLocale(); @@ -23,7 +24,7 @@ export const AverageEventDurationChart = () => { if (!isSuccess || data?.length == 0 || !startDate || !endDate || !teamId) return null; return ( - + {t("average_event_duration")} { colors={["blue"]} valueFormatter={valueFormatter} /> - + ); }; diff --git a/packages/features/insights/components/BookingStatusLineChart.tsx b/packages/features/insights/components/BookingStatusLineChart.tsx index 4bcf463188..1b4b9eccd2 100644 --- a/packages/features/insights/components/BookingStatusLineChart.tsx +++ b/packages/features/insights/components/BookingStatusLineChart.tsx @@ -1,10 +1,11 @@ -import { Card, LineChart, Title } from "@tremor/react"; +import { LineChart, Title } from "@tremor/react"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { trpc } from "@calcom/trpc"; import { useFilterContext } from "../context/provider"; import { valueFormatter } from "../lib/valueFormatter"; +import { CardInsights } from "./Card"; export const BookingStatusLineChart = () => { const { t } = useLocale(); @@ -25,7 +26,7 @@ export const BookingStatusLineChart = () => { if (!isSuccess) return null; return ( - + {t("event_trends")} { colors={["gray", "green", "blue", "red"]} valueFormatter={valueFormatter} /> - + ); }; diff --git a/packages/features/insights/components/Card.tsx b/packages/features/insights/components/Card.tsx new file mode 100644 index 0000000000..454aab3b8e --- /dev/null +++ b/packages/features/insights/components/Card.tsx @@ -0,0 +1,17 @@ +import { Card } from "@tremor/react"; + +interface ICardProps { + children: React.ReactNode; + className?: string; +} + +export const CardInsights = (props: ICardProps) => { + const { children, className = "", ...rest } = props; + + return ( + + {children} + + ); +}; + diff --git a/packages/features/insights/components/KPICard.tsx b/packages/features/insights/components/KPICard.tsx index 76bd954057..e69f77f312 100644 --- a/packages/features/insights/components/KPICard.tsx +++ b/packages/features/insights/components/KPICard.tsx @@ -1,9 +1,10 @@ -import { Card, Flex, Text, Metric, BadgeDelta } from "@tremor/react"; +import { Flex, Text, Metric, BadgeDelta } from "@tremor/react"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { Tooltip } from "@calcom/ui"; import { calculateDeltaType, colors, valueFormatter } from "../lib"; +import { CardInsights } from "./Card"; export const KPICard = ({ title, @@ -20,7 +21,7 @@ export const KPICard = ({ }) => { const { t } = useLocale(); return ( - + {title} {valueFormatter(previousMetricData.count)} @@ -40,10 +41,12 @@ export const KPICard = ({ startDate: previousDateRange.startDate, endDate: previousDateRange.endDate, })}> - {t("from_last_period")} + + {t("from_last_period")} + - + ); }; diff --git a/packages/features/insights/components/LeastBookedTeamMembersTable.tsx b/packages/features/insights/components/LeastBookedTeamMembersTable.tsx index 6501f163c0..5cf24b65c0 100644 --- a/packages/features/insights/components/LeastBookedTeamMembersTable.tsx +++ b/packages/features/insights/components/LeastBookedTeamMembersTable.tsx @@ -1,30 +1,31 @@ -import { Card, Title } from "@tremor/react"; +import { Title } from "@tremor/react"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { trpc } from "@calcom/trpc"; import { useFilterContext } from "../context/provider"; +import { CardInsights } from "./Card"; import { TotalBookingUsersTable } from "./TotalBookingUsersTable"; export const LeastBookedTeamMembersTable = () => { const { t } = useLocale(); const { filter } = useFilterContext(); - const { dateRange } = filter; + const { dateRange, selectedEventTypeId, selectedTeamId: teamId } = filter; const [startDate, endDate] = dateRange; - const { selectedTeamId: teamId } = filter; const { data, isSuccess } = trpc.viewer.insights.membersWithLeastBookings.useQuery({ startDate: startDate.toISOString(), endDate: endDate.toISOString(), teamId, + eventTypeId: selectedEventTypeId ?? undefined, }); if (!isSuccess || !startDate || !endDate || !teamId) return null; return ( - + {t("least_booked_members")} - + ); }; diff --git a/packages/features/insights/components/MostBookedTeamMembersTable.tsx b/packages/features/insights/components/MostBookedTeamMembersTable.tsx index 3fccd13254..978a1678c4 100644 --- a/packages/features/insights/components/MostBookedTeamMembersTable.tsx +++ b/packages/features/insights/components/MostBookedTeamMembersTable.tsx @@ -1,9 +1,10 @@ -import { Card, Title } from "@tremor/react"; +import { Title } from "@tremor/react"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { trpc } from "@calcom/trpc"; import { useFilterContext } from "../context/provider"; +import { CardInsights } from "./Card"; import { TotalBookingUsersTable } from "./TotalBookingUsersTable"; export const MostBookedTeamMembersTable = () => { @@ -23,9 +24,9 @@ export const MostBookedTeamMembersTable = () => { if (!isSuccess || !startDate || !endDate || !teamId) return null; return ( - + {t("most_booked_members")} - + ); }; diff --git a/packages/features/insights/components/PopularEventsTable.tsx b/packages/features/insights/components/PopularEventsTable.tsx index 27f257e8eb..05ac898097 100644 --- a/packages/features/insights/components/PopularEventsTable.tsx +++ b/packages/features/insights/components/PopularEventsTable.tsx @@ -1,9 +1,10 @@ -import { Card, Title, Table, TableBody, TableCell, TableRow, Text } from "@tremor/react"; +import { Table, TableBody, TableCell, TableRow, Text, Title } from "@tremor/react"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { trpc } from "@calcom/trpc"; import { useFilterContext } from "../context/provider"; +import { CardInsights } from "./Card"; export const PopularEventsTable = () => { const { t } = useLocale(); @@ -19,34 +20,25 @@ export const PopularEventsTable = () => { userId: selectedUserId ?? undefined, }); - if (!startDate || !endDate || !teamId) return null; + if (!isSuccess || !startDate || !endDate || !teamId || data?.length === 0) return null; return ( - + {t("popular_events")} - {isSuccess ? ( - data?.map((item) => ( - - {item.eventTypeName} - - - {item.count} - - - - )) - ) : ( - - {t("no_event_types_found")} + {data.map((item) => ( + + {item.eventTypeName} - 0 + + {item.count} + - )} + ))}
-
+ ); }; diff --git a/packages/features/insights/context/FiltersProvider.tsx b/packages/features/insights/context/FiltersProvider.tsx index 00516c78f7..f24fe39dde 100644 --- a/packages/features/insights/context/FiltersProvider.tsx +++ b/packages/features/insights/context/FiltersProvider.tsx @@ -1,4 +1,6 @@ +import { useRouter } from "next/router"; import { useState } from "react"; +import { z } from "zod"; import dayjs from "@calcom/dayjs"; @@ -6,19 +8,60 @@ import type { FilterContextType } from "./provider"; import { FilterProvider } from "./provider"; export function FiltersProvider({ children }: { children: React.ReactNode }) { + // useRouter to get initial values from query params + const router = useRouter(); + const { startTime, endTime, teamId, userId, eventTypeId, filter } = router.query; + const querySchema = z.object({ + startTime: z.string().optional(), + endTime: z.string().optional(), + teamId: z.coerce.number().optional(), + userId: z.coerce.number().optional(), + eventTypeId: z.coerce.number().optional(), + filter: z.enum(["event-type", "user"]).optional(), + }); + + let startTimeParsed, endTimeParsed, teamIdParsed, userIdParsed, eventTypeIdParsed, filterParsed; + + const safe = querySchema.safeParse({ + startTime, + endTime, + teamId, + userId, + eventTypeId, + filter, + }); + + if (!safe.success) { + console.error("Failed to parse query params"); + } else { + startTimeParsed = safe.data.startTime; + endTimeParsed = safe.data.endTime; + teamIdParsed = safe.data.teamId; + userIdParsed = safe.data.userId; + eventTypeIdParsed = safe.data.eventTypeId; + filterParsed = safe.data.filter; + } + // TODO: Sync insight filters with URL parameters const [selectedTimeView, setSelectedTimeView] = useState("week"); - const [selectedUserId, setSelectedUserId] = useState(null); - const [selectedTeamId, setSelectedTeamId] = useState(null); - const [selectedEventTypeId, setSelectedEventTypeId] = - useState(null); - const [selectedFilter, setSelectedFilter] = useState(null); + const [selectedUserId, setSelectedUserId] = useState( + userIdParsed || null + ); + const [selectedTeamId, setSelectedTeamId] = useState( + teamIdParsed || null + ); + const [selectedEventTypeId, setSelectedEventTypeId] = useState< + FilterContextType["filter"]["selectedEventTypeId"] + >(eventTypeIdParsed || null); + const [selectedFilter, setSelectedFilter] = useState( + filterParsed ? [filterParsed] : null + ); const [selectedTeamName, setSelectedTeamName] = useState(null); const [dateRange, setDateRange] = useState([ - dayjs().subtract(1, "month"), - dayjs(), + startTimeParsed ? dayjs(startTimeParsed) : dayjs().subtract(1, "month"), + endTimeParsed ? dayjs(endTimeParsed) : dayjs(), "t", ]); return ( @@ -33,13 +76,58 @@ export function FiltersProvider({ children }: { children: React.ReactNode }) { selectedEventTypeId, selectedFilter, }, - setSelectedFilter: (filter) => setSelectedFilter(filter), - setDateRange: (dateRange) => setDateRange(dateRange), + setSelectedFilter: (filter) => { + setSelectedFilter(filter); + const userId = filter?.[0] === "user" ? selectedUserId : undefined; + const eventTypeId = filter?.[0] === "event-type" ? selectedEventTypeId : undefined; + router.push({ + query: { + ...router.query, + filter: filter?.[0], + userId, + eventTypeId, + }, + }); + }, + setDateRange: (dateRange) => { + setDateRange(dateRange); + router.push({ + query: { + ...router.query, + startTime: dateRange[0].toISOString(), + endTime: dateRange[1].toISOString(), + }, + }); + }, setSelectedTimeView: (selectedTimeView) => setSelectedTimeView(selectedTimeView), - setSelectedUserId: (selectedUserId) => setSelectedUserId(selectedUserId), - setSelectedTeamId: (selectedTeamId) => setSelectedTeamId(selectedTeamId), + setSelectedUserId: (selectedUserId) => { + setSelectedUserId(selectedUserId); + router.push({ + query: { + ...router.query, + userId: selectedUserId, + }, + }); + }, + setSelectedTeamId: (selectedTeamId) => { + setSelectedTeamId(selectedTeamId); + router.push({ + query: { + ...router.query, + teamId: selectedTeamId, + }, + }); + }, setSelectedTeamName: (selectedTeamName) => setSelectedTeamName(selectedTeamName), - setSelectedEventTypeId: (selectedEventTypeId) => setSelectedEventTypeId(selectedEventTypeId), + setSelectedEventTypeId: (selectedEventTypeId) => { + setSelectedEventTypeId(selectedEventTypeId); + router.push({ + query: { + ...router.query, + eventTypeId: selectedEventTypeId, + }, + }); + }, }}> {children} diff --git a/packages/features/insights/filters/DateSelect.css b/packages/features/insights/filters/DateSelect.css new file mode 100644 index 0000000000..221a23f6d7 --- /dev/null +++ b/packages/features/insights/filters/DateSelect.css @@ -0,0 +1,7 @@ +.custom-date > .tremor-DateRangePicker-root > .tremor-DateRangePicker-button { + box-shadow: none; +} + +.tremor-DateRangePicker-calendarButton, .tremor-DateRangePicker-dropdownButton { + @apply dark:bg-darkgray-100 dark:border-darkgray-300 border-gray-300 bg-white text-sm leading-4 placeholder:text-sm placeholder:font-normal focus-within:ring-0 focus-within:ring-gray-800 hover:border-gray-400 dark:focus-within:ring-darkgray-900 +} diff --git a/packages/features/insights/filters/DateSelect.tsx b/packages/features/insights/filters/DateSelect.tsx index dc97c97a44..d3477c331e 100644 --- a/packages/features/insights/filters/DateSelect.tsx +++ b/packages/features/insights/filters/DateSelect.tsx @@ -4,6 +4,7 @@ import dayjs from "@calcom/dayjs"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { useFilterContext } from "../context/provider"; +import "./DateSelect.css"; type RangeType = "tdy" | "w" | "t" | "m" | "y" | undefined | null; @@ -15,44 +16,49 @@ export const DateSelect = () => { const startValue = startDate?.toDate() || null; const endValue = endDate?.toDate() || null; return ( - { - const [selected, ...rest] = datesArray; - const [start, end, range] = datesArray; - // If range has value and it's of type RangeType +
+ { + const [selected, ...rest] = datesArray; + const [start, end, range] = datesArray; + // If range has value and it's of type RangeType - if (range && (range === "tdy" || range === "w" || range === "t" || range === "m" || range === "y")) { - setDateRange([dayjs(start), dayjs(end), range]); - return; - } else if (start && !end) { - // If only start time has value that means selected date should push to dateRange with last value null - const currentDates = filter.dateRange; - // remove last position of array - currentDates.pop(); - // push new value to array - currentDates.push(dayjs(selected)); - // if lenght > 2 then remove first value - if (currentDates.length > 2) { - currentDates.shift(); + if ( + range && + (range === "tdy" || range === "w" || range === "t" || range === "m" || range === "y") + ) { + setDateRange([dayjs(start), dayjs(end), range]); + return; + } else if (start && !end) { + // If only start time has value that means selected date should push to dateRange with last value null + const currentDates = filter.dateRange; + // remove last position of array + currentDates.pop(); + // push new value to array + currentDates.push(dayjs(selected)); + // if lenght > 2 then remove first value + if (currentDates.length > 2) { + currentDates.shift(); + } + + setDateRange([currentDates[0], currentDates[1], null]); + + return; } - setDateRange([currentDates[0], currentDates[1], null]); - - return; - } - - // If range has value and it's of type RangeType - }} - options={undefined} - enableDropdown={true} - placeholder={t("select_date_range")} - enableYearPagination={true} - minDate={currentDate.subtract(2, "year").toDate()} - maxDate={currentDate.toDate()} - color="blue" - className="h-[42px] max-w-sm" - /> + // If range has value and it's of type RangeType + }} + options={undefined} + enableDropdown={true} + placeholder={t("select_date_range")} + enableYearPagination={true} + minDate={currentDate.subtract(2, "year").toDate()} + maxDate={currentDate.toDate()} + color="gray" + className="h-[42px] max-w-sm" + /> +
); }; diff --git a/packages/features/insights/filters/FilterType.tsx b/packages/features/insights/filters/FilterType.tsx index 37bf04dfa3..fd01efa067 100644 --- a/packages/features/insights/filters/FilterType.tsx +++ b/packages/features/insights/filters/FilterType.tsx @@ -8,7 +8,8 @@ type Option = { value: "event-type" | "user"; label: string }; export const FilterType = () => { const { t } = useLocale(); - const { setSelectedFilter, setSelectedUserId, setSelectedEventTypeId } = useFilterContext(); + const { setSelectedFilter, setSelectedUserId, setSelectedEventTypeId, filter } = useFilterContext(); + const { selectedFilter } = filter; const filterOptions: Option[] = [ { @@ -21,20 +22,19 @@ export const FilterType = () => { }, ]; + const filterValue = selectedFilter + ? filterOptions.find((option) => option.value === selectedFilter[0]) + : undefined; + return ( - isMulti={false} - isSearchable={false} options={filterOptions} - onChange={(input) => { - if (input) { + value={filterValue} + defaultValue={filterValue} + onChange={(newValue) => { + if (newValue) { // This can multiple values, but for now we only want to have one filter active at a time - setSelectedFilter([input.value]); - if (input.value === "event-type") { - setSelectedUserId(null); - } else if (input.value === "user") { - setSelectedEventTypeId(null); - } + setSelectedFilter([newValue.value]); } }} className="w-32 min-w-[130px]" diff --git a/packages/features/insights/filters/index.tsx b/packages/features/insights/filters/index.tsx index 6a63b5e68b..e172f8271c 100644 --- a/packages/features/insights/filters/index.tsx +++ b/packages/features/insights/filters/index.tsx @@ -1,12 +1,43 @@ +import { useFilterContext } from "@calcom/features/insights/context/provider"; +import { useLocale } from "@calcom/lib/hooks/useLocale"; +import { Button, Tooltip } from "@calcom/ui"; +import { FiX } from "@calcom/ui/components/icon"; + import { DateSelect } from "./DateSelect"; import { EventTypeListInTeam } from "./EventTypeListInTeam"; import { FilterType } from "./FilterType"; import { TeamList } from "./TeamList"; import { UserListInTeam } from "./UsersListInTeam"; +const ClearFilters = () => { + const { t } = useLocale(); + const { filter, setSelectedUserId, setSelectedFilter, setSelectedEventTypeId } = useFilterContext(); + const { selectedFilter } = filter; + + if (!selectedFilter || selectedFilter?.length < 1) return null; + + return ( + +