diff --git a/packages/app-store/routing-forms/pages/app-routing.client-config.tsx b/packages/app-store/routing-forms/pages/app-routing.client-config.tsx index fb4fe9a8ddb139..24bf4e54127d08 100644 --- a/packages/app-store/routing-forms/pages/app-routing.client-config.tsx +++ b/packages/app-store/routing-forms/pages/app-routing.client-config.tsx @@ -2,6 +2,5 @@ export const routingFormsComponents = { "form-edit": () => import("./form-edit/[...appPages]").then((mod) => mod.default), "route-builder": () => import("./route-builder/[...appPages]").then((mod) => mod.default), "routing-link": () => import("./routing-link/[...appPages]").then((mod) => mod.default), - reporting: () => import("./reporting/[...appPages]").then((mod) => mod.default), "incomplete-booking": () => import("./incomplete-booking/[...appPages]").then((mod) => mod.default), }; diff --git a/packages/app-store/routing-forms/pages/reporting/[...appPages].tsx b/packages/app-store/routing-forms/pages/reporting/[...appPages].tsx deleted file mode 100644 index 7a7b766cc665bc..00000000000000 --- a/packages/app-store/routing-forms/pages/reporting/[...appPages].tsx +++ /dev/null @@ -1,292 +0,0 @@ -"use client"; - -import React, { useCallback, useRef, useState, useEffect } from "react"; -import type { - BuilderProps, - Config, - ImmutableTree, - JsonLogicResult, - JsonTree, -} from "react-awesome-query-builder"; -import { Builder, Query, Utils as QbUtils } from "react-awesome-query-builder"; - -import { downloadAsCsv, sanitizeValue } from "@calcom/lib/csvUtils"; -import { useInViewObserver } from "@calcom/lib/hooks/useInViewObserver"; -import { useLocale } from "@calcom/lib/hooks/useLocale"; -import { trpc } from "@calcom/trpc/react"; -import type { inferSSRProps } from "@calcom/types/inferSSRProps"; -import classNames from "@calcom/ui/classNames"; -import { Button } from "@calcom/ui/components/button"; -import { showToast } from "@calcom/ui/components/toast"; - -import SingleForm from "../../components/SingleForm"; -import type { getServerSidePropsForSingleFormView as getServerSideProps } from "../../components/getServerSidePropsSingleForm"; -import { - withRaqbSettingsAndWidgets, - ConfigFor, -} from "../../components/react-awesome-query-builder/config/uiConfig"; -import type { JsonLogicQuery } from "../../jsonLogicToPrisma"; -import { - getQueryBuilderConfigForFormFields, - type FormFieldsQueryBuilderConfigWithRaqbFields, -} from "../../lib/getQueryBuilderConfig"; - -const Result = ({ - formName, - formId, - jsonLogicQuery, -}: { - formName: string; - formId: string; - jsonLogicQuery: JsonLogicQuery | null; -}) => { - const { t } = useLocale(); - const [isDownloading, setIsDownloading] = useState(false); - - const { isPending, status, data, isFetching, error, isFetchingNextPage, hasNextPage, fetchNextPage } = - trpc.viewer.appRoutingForms.report.useInfiniteQuery( - { - formId: formId, - // Send jsonLogicQuery only if it's a valid logic, otherwise send a logic with no query. - jsonLogicQuery: jsonLogicQuery?.logic - ? jsonLogicQuery - : { - logic: {}, - }, - }, - { - getNextPageParam: (lastPage) => lastPage.nextCursor, - } - ); - const exportQuery = trpc.viewer.appRoutingForms.report.useInfiniteQuery( - { - limit: 100, // 100 is max - formId: formId, - // Send jsonLogicQuery only if it's a valid logic, otherwise send a logic with no query. - jsonLogicQuery: jsonLogicQuery?.logic - ? jsonLogicQuery - : { - logic: {}, - }, - }, - { - getNextPageParam: (lastPage) => lastPage.nextCursor, - enabled: false, - } - ); - const buttonInView = useInViewObserver(() => { - if (!isFetching && hasNextPage && status === "success") { - fetchNextPage(); - } - }); - - const headers = useRef(null); - - if (!isPending && !data) { - return
Error loading report {error?.message}
; - } - headers.current = (data?.pages && data?.pages[0]?.headers) || headers.current; - - const numberOfRows = data?.pages.reduce((total, page) => total + (page.responses?.length || 0), 0); - const downloadButtonDisabled = !numberOfRows || !data || !headers.current || isDownloading; - - const handleDownload = async () => { - try { - setIsDownloading(true); - - if (downloadButtonDisabled) { - return; - } - - const result = await exportQuery.refetch(); - if (!result.data) { - throw new Error("There are no routing forms found."); - } - const allRows = result.data.pages.flatMap((page) => - page.responses.map((response) => `${response.map((value) => sanitizeValue(value)).join(",")}\n`) - ); - let lastPage = result.data.pages[result.data.pages.length - 1]; - while (lastPage.nextCursor) { - const nextPage = await exportQuery.fetchNextPage(); - if (!nextPage.data) { - break; - } - const latestPageItems = nextPage.data.pages[nextPage.data.pages.length - 1].responses ?? []; - allRows.push( - ...latestPageItems.map((response) => `${response.map((value) => sanitizeValue(value)).join(",")}\n`) - ); - lastPage = nextPage.data.pages[nextPage.data.pages.length - 1]; - } - - const header = `${(headers.current ?? []).map((value) => sanitizeValue(value)).join(",")}\n`; - const csvRaw = header + allRows.join(""); - const filename = `${formName}_${new Date().toISOString().split("T")[0]}.csv`; // e.g., ${FormName}_2024-10-30.csv - downloadAsCsv(csvRaw, filename); - } catch (error) { - showToast(`Error: ${error}`, "error"); - } finally { - setIsDownloading(false); - } - }; - - return ( -
- {!isPending && ( -
- -
- {`${numberOfRows} ${numberOfRows === 1 ? t("row") : t("rows")}`} -
-
- )} - - - {headers.current?.map((header, index) => ( - - ))} - - {!isPending && - data?.pages.map((page) => { - return page.responses?.map((responses, rowIndex) => { - const isLastRow = page.responses.length - 1 === rowIndex; - return ( - - {responses.map((r, columnIndex) => { - const isLastColumn = columnIndex === responses.length - 1; - return ( - - ); - })} - - ); - }); - })} -
- {header} -
- {r} -
- {isPending ?
{t("loading")}
: ""} - {hasNextPage && ( - - )} -
- ); -}; - -const getInitialQuery = (config: ReturnType) => { - const uuid = QbUtils.uuid(); - const queryValue: JsonTree = { id: uuid, type: "group" } as JsonTree; - const tree = QbUtils.checkTree(QbUtils.loadTree(queryValue), config as unknown as Config); - return { - state: { tree, config }, - queryValue, - }; -}; - -const Reporter = ({ form }: { form: inferSSRProps["form"] }) => { - const config = getQueryBuilderConfigForFormFields(form, true); - const [query, setQuery] = useState(getInitialQuery(config)); - const [jsonLogicQuery, setJsonLogicQuery] = useState(null); - const onChange = (immutableTree: ImmutableTree, config: FormFieldsQueryBuilderConfigWithRaqbFields) => { - const jsonTree = QbUtils.getTree(immutableTree); - setQuery(() => { - const newValue = { - state: { tree: immutableTree, config: config }, - queryValue: jsonTree, - }; - setJsonLogicQuery(QbUtils.jsonLogicFormat(newValue.state.tree, config as unknown as Config)); - return newValue; - }); - }; - - const renderBuilder = useCallback( - (props: BuilderProps) => ( -
-
- -
-
- ), - [] - ); - return ( -
- { - onChange(immutableTree, config as unknown as FormFieldsQueryBuilderConfigWithRaqbFields); - }} - renderBuilder={renderBuilder} - /> - -
- ); -}; - -export default function ReporterWrapper({ - permissions, - ...props -}: inferSSRProps & { appUrl: string }) { - const [isClient, setIsClient] = useState(false); - - useEffect(() => { - // It isn't possible to render Reporter without hydration errors if it is rendered on the server. - // This is because the RAQB generates some dynamic ids on elements which change b/w client and server. - // This is a workaround to render the Reporter on the client only. - setIsClient(true); - }, []); - - return ( - ( -
- {isClient && } -
- )} - /> - ); -} diff --git a/packages/app-store/routing-forms/playwright/tests/basic.e2e.ts b/packages/app-store/routing-forms/playwright/tests/basic.e2e.ts index bf0d631f0ce261..40ab97415fab6b 100644 --- a/packages/app-store/routing-forms/playwright/tests/basic.e2e.ts +++ b/packages/app-store/routing-forms/playwright/tests/basic.e2e.ts @@ -408,52 +408,6 @@ test.describe("Routing Forms", () => { // Log back in to view form responses. await user.apiLogin(); - await page.goto(`/routing-forms/reporting/${routingForm.id}`); - - const headerEls = page.locator("[data-testid='reporting-header'] th"); - - // Wait for the headers to be visible(will automaically wait for getting response from backend) along with it the rows are rendered. - await headerEls.first().waitFor(); - - const numHeaderEls = await headerEls.count(); - const headers = []; - for (let i = 0; i < numHeaderEls; i++) { - headers.push(await headerEls.nth(i).innerText()); - } - - const responses = []; - const responseRows = page.locator("[data-testid='reporting-row']"); - const numResponseRows = await responseRows.count(); - for (let i = 0; i < numResponseRows; i++) { - const rowLocator = responseRows.nth(i).locator("td"); - const numRowEls = await rowLocator.count(); - const rowResponses = []; - for (let j = 0; j < numRowEls; j++) { - rowResponses.push(await rowLocator.nth(j).innerText()); - } - responses.push(rowResponses); - } - - expect(headers).toEqual([ - "Test field", - "Multi Select(with Legacy `selectText`)", - "Multi Select", - "Legacy Select", - "Select", - // TODO: Find a way to incorporate Routed To and Booked At into the report - // @see https://github.com/calcom/cal.com/pull/17229 - "Routed To", - "Assignment Reason", - "Booked At", - "Submitted At", - ]); - /* Last two columns are "Routed To" and "Booked At" */ - expect(responses).toEqual([ - ["custom-page", "Option-2", "Option-2", "Option-2", "Option-2", "", "", "", expect.any(String)], - ["external-redirect", "Option-2", "Option-2", "Option-2", "Option-2", "", "", "", expect.any(String)], - ["event-routing", "Option-2", "Option-2", "Option-2", "Option-2", "", "", "", expect.any(String)], - ]); - await page.goto(`apps/routing-forms/route-builder/${routingForm.id}`); const downloadPromise = page.waitForEvent("download"); diff --git a/packages/features/shell/Shell.tsx b/packages/features/shell/Shell.tsx index 8f966c03ad03a9..82a43ee8c95449 100644 --- a/packages/features/shell/Shell.tsx +++ b/packages/features/shell/Shell.tsx @@ -27,7 +27,6 @@ import { useAppTheme } from "./useAppTheme"; const Layout = (props: LayoutProps) => { const { banners, bannersHeight } = useBanners(); const pathname = usePathname(); - const isFullPageWithoutSidebar = pathname?.startsWith("/apps/routing-forms/reporting/"); useFormbricks(); @@ -40,9 +39,7 @@ const Layout = (props: LayoutProps) => {
- {banners && !props.isPlatformUser && !isFullPageWithoutSidebar && ( - - )} + {banners && !props.isPlatformUser && }
{props.SidebarContainer ? (