refactors

This commit is contained in:
Dhruwang
2026-03-02 11:13:11 +05:30
parent 55a795da97
commit 00c0e5070b
6 changed files with 69 additions and 68 deletions
@@ -1,7 +1,5 @@
"use server";
// eslint-disable-next-line
// TODO: remove revalidatePath and use revalidateTag instead once this has become stable: https://nextjs.org/docs/app/api-reference/directives/use-cache#usage
import { revalidatePath } from "next/cache";
import { z } from "zod";
import { ZWidgetLayout } from "@formbricks/types/analysis";
@@ -4,7 +4,9 @@ import { Loader2Icon } from "lucide-react";
import { useEffect, useState } from "react";
import toast from "react-hot-toast";
import { useTranslation } from "react-i18next";
import { getFormattedErrorMessage } from "@/lib/utils/helper";
import { getChartsAction } from "@/modules/ee/analysis/charts/actions";
import { addChartToDashboardAction } from "@/modules/ee/analysis/dashboards/actions";
import { Alert, AlertDescription, AlertTitle } from "@/modules/ui/components/alert";
import { Button } from "@/modules/ui/components/button";
import {
@@ -17,7 +19,6 @@ import {
DialogTitle,
} from "@/modules/ui/components/dialog";
import { MultiSelect } from "@/modules/ui/components/multi-select";
import { addChartToDashboardAction } from "../actions";
interface AddExistingChartsDialogProps {
open: boolean;
@@ -59,7 +60,8 @@ export function AddExistingChartsDialog({
const availableCharts = result.data.filter((chart) => !existingChartIds.includes(chart.id));
setChartOptions(availableCharts.map((chart) => ({ value: chart.id, label: chart.name })));
} else {
toast.error(result?.serverError || t("environments.analysis.dashboards.charts_load_failed"));
const errorMessage = getFormattedErrorMessage(result);
toast.error(errorMessage);
}
} catch {
toast.error(t("environments.analysis.dashboards.charts_load_failed"));
@@ -6,9 +6,9 @@ import { useState } from "react";
import toast from "react-hot-toast";
import { useTranslation } from "react-i18next";
import { getFormattedErrorMessage } from "@/lib/utils/helper";
import { createDashboardAction } from "@/modules/ee/analysis/dashboards/actions";
import { CreateDashboardDialog } from "@/modules/ee/analysis/dashboards/components/create-dashboard-dialog";
import { Button } from "@/modules/ui/components/button";
import { createDashboardAction } from "../actions";
import { CreateDashboardDialog } from "./create-dashboard-dialog";
interface CreateDashboardButtonProps {
environmentId: string;
@@ -5,10 +5,11 @@ import { useRouter } from "next/navigation";
import { useState } from "react";
import toast from "react-hot-toast";
import { useTranslation } from "react-i18next";
import { getFormattedErrorMessage } from "@/lib/utils/helper";
import { deleteDashboardAction } from "@/modules/ee/analysis/dashboards/actions";
import { AddExistingChartsDialog } from "@/modules/ee/analysis/dashboards/components/add-existing-charts-dialog";
import { DeleteDialog } from "@/modules/ui/components/delete-dialog";
import { IconBar } from "@/modules/ui/components/iconbar";
import { deleteDashboardAction } from "../actions";
import { AddExistingChartsDialog } from "./add-existing-charts-dialog";
interface DashboardControlBarProps {
environmentId: string;
@@ -51,12 +52,11 @@ export const DashboardControlBar = ({
router.push(`/environments/${environmentId}/analysis/dashboards`);
toast.success(t("environments.analysis.dashboards.delete_success"));
} else {
toast.error(result?.serverError || t("environments.analysis.dashboards.delete_failed"));
const errorMessage = getFormattedErrorMessage(result);
toast.error(errorMessage);
}
} catch (error) {
const message =
error instanceof Error ? error.message : t("environments.analysis.dashboards.delete_failed");
toast.error(message);
} catch {
toast.error(t("environments.analysis.dashboards.delete_failed"));
} finally {
setIsDeleting(false);
setDeleteDialogOpen(false);
@@ -10,16 +10,17 @@ import toast from "react-hot-toast";
import { useTranslation } from "react-i18next";
import "react-resizable/css/styles.css";
import type { TChartQuery } from "@formbricks/types/analysis";
import { getFormattedErrorMessage } from "@/lib/utils/helper";
import { DashboardControlBar } from "@/modules/ee/analysis/dashboards/components/dashboard-control-bar";
import { DashboardPageHeader } from "@/modules/ee/analysis/dashboards/components/dashboard-page-header";
import { DashboardWidget } from "@/modules/ee/analysis/dashboards/components/dashboard-widget";
import { DashboardWidgetData } from "@/modules/ee/analysis/dashboards/components/dashboard-widget-data";
import { DashboardWidgetSkeleton } from "@/modules/ee/analysis/dashboards/components/dashboard-widget-skeleton";
import type { TChartDataRow, TDashboardDetail, TDashboardWidget } from "@/modules/ee/analysis/types/analysis";
import { EmptyState } from "@/modules/ui/components/empty-state";
import { GoBackButton } from "@/modules/ui/components/go-back-button";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
import { updateDashboardAction, updateWidgetLayoutsAction } from "../actions";
import { DashboardControlBar } from "./dashboard-control-bar";
import { DashboardPageHeader } from "./dashboard-page-header";
import { DashboardWidget } from "./dashboard-widget";
import { DashboardWidgetData } from "./dashboard-widget-data";
import { DashboardWidgetSkeleton } from "./dashboard-widget-skeleton";
const ROW_HEIGHT = 80;
@@ -30,7 +31,7 @@ interface DashboardDetailClientProps {
isReadOnly: boolean;
}
function widgetsToLayout(widgets: TDashboardWidget[]): LayoutItem[] {
const widgetsToLayout = (widgets: TDashboardWidget[]): LayoutItem[] => {
return widgets.map((widget) => ({
i: widget.id,
x: widget.layout.x,
@@ -42,9 +43,9 @@ function widgetsToLayout(widgets: TDashboardWidget[]): LayoutItem[] {
maxW: 12,
maxH: 8,
}));
}
};
function widgetLayoutsChanged(current: TDashboardWidget[], original: TDashboardWidget[]): boolean {
const widgetLayoutsChanged = (current: TDashboardWidget[], original: TDashboardWidget[]): boolean => {
if (current.length !== original.length) return true;
return current.some((widget, i) => {
const orig = original[i];
@@ -57,9 +58,9 @@ function widgetLayoutsChanged(current: TDashboardWidget[], original: TDashboardW
widget.order !== orig.order
);
});
}
};
function applyLayoutToWidgets(widgets: TDashboardWidget[], newLayout: Layout): TDashboardWidget[] {
const applyLayoutToWidgets = (widgets: TDashboardWidget[], newLayout: Layout): TDashboardWidget[] => {
let changed = false;
const updated = widgets.map((widget) => {
const layoutItem = newLayout.find((l) => l.i === widget.id);
@@ -87,33 +88,35 @@ function applyLayoutToWidgets(widgets: TDashboardWidget[], newLayout: Layout): T
});
return changed ? updated : widgets;
}
};
const MemoizedWidgetContent = memo(function WidgetContent({
widget,
dataPromise,
}: Readonly<{
widget: TDashboardWidget;
dataPromise?: Promise<{ data: TChartDataRow[]; query: TChartQuery } | { error: string }>;
}>) {
if (widget.chart && dataPromise) {
return (
<Suspense
fallback={
<Delay ms={200}>
<DashboardWidgetSkeleton />
</Delay>
}>
<DashboardWidgetData
dataPromise={dataPromise}
chartType={widget.chart.type}
query={widget.chart.query}
/>
</Suspense>
);
const MemoizedWidgetContent = memo(
({
widget,
dataPromise,
}: Readonly<{
widget: TDashboardWidget;
dataPromise?: Promise<{ data: TChartDataRow[]; query: TChartQuery } | { error: string }>;
}>) => {
if (widget.chart && dataPromise) {
return (
<Suspense
fallback={
<Delay ms={200}>
<DashboardWidgetSkeleton />
</Delay>
}>
<DashboardWidgetData
dataPromise={dataPromise}
chartType={widget.chart.type}
query={widget.chart.query}
/>
</Suspense>
);
}
return <DashboardWidgetSkeleton />;
}
return <DashboardWidgetSkeleton />;
});
);
const MemoizedWidgetItem = memo(function WidgetItem({
widget,
@@ -143,7 +146,7 @@ export function DashboardDetailClient({
}: Readonly<DashboardDetailClientProps>) {
const router = useRouter();
const { t } = useTranslation();
const { width, containerRef, mounted } = useContainerWidth({ initialWidth: 1200 });
const { width, containerRef, mounted } = useContainerWidth();
const [isEditing, setIsEditing] = useState(false);
const [isSaving, setIsSaving] = useState(false);
@@ -199,9 +202,8 @@ export function DashboardDetailClient({
});
if (!dashboardResult?.data) {
toast.error(
dashboardResult?.serverError || t("environments.analysis.dashboards.dashboard_update_failed")
);
const errorMessage = getFormattedErrorMessage(dashboardResult);
toast.error(errorMessage);
setIsSaving(false);
return;
}
@@ -221,9 +223,8 @@ export function DashboardDetailClient({
});
if (!widgetsResult?.data) {
toast.error(
widgetsResult?.serverError || t("environments.analysis.dashboards.widget_layouts_save_failed")
);
const errorMessage = getFormattedErrorMessage(widgetsResult);
toast.error(errorMessage);
setIsSaving(false);
return;
}
@@ -235,10 +236,8 @@ export function DashboardDetailClient({
setDraftWidgets(null);
setIsEditing(false);
});
} catch (error) {
const message =
error instanceof Error ? error.message : t("environments.analysis.dashboards.dashboard_save_failed");
toast.error(message);
} catch {
toast.error(t("environments.analysis.dashboards.dashboard_save_failed"));
} finally {
setIsSaving(false);
}
@@ -1,7 +1,6 @@
"use client";
import { CopyIcon, MoreVertical, SquarePenIcon, TrashIcon } from "lucide-react";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { useState } from "react";
import toast from "react-hot-toast";
@@ -44,9 +43,8 @@ export const DashboardDropdownMenu = ({
toast.success(t("environments.analysis.dashboards.duplicate_success"));
router.refresh();
} else {
toast.error(
getFormattedErrorMessage(result) || t("environments.analysis.dashboards.duplicate_failed")
);
const errorMessage = getFormattedErrorMessage(result);
toast.error(errorMessage);
}
} catch {
toast.error(t("environments.analysis.dashboards.duplicate_failed"));
@@ -64,7 +62,8 @@ export const DashboardDropdownMenu = ({
toast.success(t("environments.analysis.dashboards.delete_success"));
router.refresh();
} else {
toast.error(getFormattedErrorMessage(result) || t("environments.analysis.dashboards.delete_failed"));
const errorMessage = getFormattedErrorMessage(result);
toast.error(errorMessage);
}
} catch {
toast.error(t("environments.analysis.dashboards.delete_failed"));
@@ -84,10 +83,13 @@ export const DashboardDropdownMenu = ({
</DropdownMenuTrigger>
<DropdownMenuContent className="inline-block w-auto min-w-max" align="end">
<DropdownMenuGroup>
<DropdownMenuItem icon={<SquarePenIcon className="size-4" />} asChild>
<Link href={`/environments/${environmentId}/analysis/dashboards/${dashboardId}`}>
{t("common.edit")}
</Link>
<DropdownMenuItem
icon={<SquarePenIcon className="size-4" />}
onClick={() => {
setIsDropDownOpen(false);
router.push(`/environments/${environmentId}/analysis/dashboards/${dashboardId}`);
}}>
{t("common.edit")}
</DropdownMenuItem>
<DropdownMenuItem