refactor: update chart creation and filtering components

- Standardized chart creation button text to lowercase for consistency.
- Enhanced filtering logic by adding AND/OR options and improved descriptions.
- Removed unused ChartBuilderGuide component to streamline the chart creation process.
- Updated dimensions and filters panels to utilize alerts for better user guidance.
- Adjusted layout and styling for improved user experience in chart creation and editing views.
This commit is contained in:
Johannes
2026-03-02 16:51:28 -03:00
parent 3123a5b401
commit a62b315d5a
12 changed files with 47 additions and 152 deletions
+10 -5
View File
@@ -642,6 +642,7 @@
"ai_query_placeholder": "e.g. How many users signed up last week?",
"ai_query_section_description": "Describe what you want to see and let AI build the chart.",
"ai_query_section_title": "Ask your data",
"and_filter_logic": "AND",
"apply_changes": "Apply Changes",
"chart": "Chart",
"chart_added_to_dashboard": "Chart added to dashboard!",
@@ -668,8 +669,9 @@
"configure_title": "Configure Chart",
"configure_type_label": "Chart Type",
"contains": "contains",
"create_chart": "Create Chart",
"create_chart": "Create chart",
"create_chart_description": "Use AI to generate a chart or build one manually.",
"create_chart_with_ai": "Create chart with AI",
"custom_range": "Custom Range",
"dashboard": "Dashboard",
"dashboard_select_placeholder": "Select a dashboard",
@@ -685,7 +687,7 @@
"date_range": "Date Range",
"delete_chart_confirmation": "Are you sure you want to delete this chart?",
"dimensions": "Dimensions",
"dimensions_toggle_description": "Group data by categories. Order matters for multi-dimensional charts.",
"dimensions_toggle_description": "Group data by sentiment, question type, and other dimensions.",
"edit_chart_description": "View and edit your chart configuration.",
"edit_chart_title": "Edit Chart",
"enable_time_dimension": "Enable Time Dimension",
@@ -715,6 +717,7 @@
"field_label_source_type": "Source Type",
"field_label_topic": "Topic",
"field_label_user_identifier": "User Identifier",
"filter_data": "Filter data",
"filters": "Filters",
"filters_toggle_description": "Only include data that meets the following conditions.",
"generate_chart": "Generate Chart",
@@ -728,7 +731,8 @@
"greater_than": "greater than",
"greater_than_or_equal": "greater than or equal",
"group_by": "Group By",
"group_by_description": "Select dimensions to break down your data. The order matters for multi-dimensional charts.",
"group_by_description": "Break down your data by one or more dimensions. The order is important if you choose multiple dimensions.",
"group_data": "Group data",
"guide_button": "View field guide",
"guide_chart_type": "Chart type",
"guide_chart_type_desc": "How the data is visualized: Area, Bar, Line, Pie, or Big Number. Choose based on what you want to show (trends, comparisons, parts of a whole, etc.).",
@@ -781,7 +785,8 @@
"showing_first_n_of": "Showing first {{n}} of {{count}} rows",
"start_date": "Start date",
"time_dimension": "Time Dimension",
"time_dimension_toggle_description": "Add time-based grouping for trends over time."
"time_dimension_title": "Add time-based grouping",
"time_dimension_toggle_description": "Monitor trends over time."
},
"dashboards": {
"add_count_charts": "Add {count} chart(s)",
@@ -789,7 +794,7 @@
"charts_add_partial_failure": "Failed to add {count} chart(s)",
"charts_added_to_dashboard": "Charts added to dashboard",
"charts_load_failed": "Failed to load charts",
"create_dashboard": "Create Dashboard",
"create_dashboard": "Create dashboard",
"create_dashboard_description": "Enter a name for your new dashboard.",
"create_failed": "Failed to create dashboard",
"create_success": "Dashboard created successfully!",
@@ -5,7 +5,7 @@ import { Button } from "@/modules/ui/components/button";
export const BackToLoginButton = async () => {
const t = await getTranslate();
return (
<Button variant="secondary" className="w-full justify-center">
<Button variant="default" className="w-full justify-center">
<Link href="/auth/login" className="h-full w-full">
{t("auth.signup.log_in")}
</Link>
@@ -5,7 +5,6 @@ import toast from "react-hot-toast";
import { useTranslation } from "react-i18next";
import type { TChartQuery } from "@formbricks/types/analysis";
import { AdvancedChartPreview } from "@/modules/ee/analysis/charts/components/advanced-chart-preview";
import { ChartBuilderGuide } from "@/modules/ee/analysis/charts/components/chart-builder-guide";
import { ChartTypeSelector } from "@/modules/ee/analysis/charts/components/chart-type-selector";
import { DimensionsPanel } from "@/modules/ee/analysis/charts/components/dimensions-panel";
import { FiltersPanel } from "@/modules/ee/analysis/charts/components/filters-panel";
@@ -132,7 +131,6 @@ export function AdvancedChartBuilder({
<div className="mx-1 space-y-2">
{!hidePreview && (
<>
<ChartBuilderGuide />
<ChartTypeSelector selectedChartType={chartType} onChartTypeSelect={() => {}} />
</>
)}
@@ -151,7 +149,7 @@ export function AdvancedChartBuilder({
if (!checked) dispatch({ type: ACTION.SET_DIMENSIONS, payload: [] });
}}
htmlId="chart-dimensions-toggle"
title={t("environments.analysis.charts.dimensions")}
title={t("environments.analysis.charts.group_data")}
description={t("environments.analysis.charts.dimensions_toggle_description")}
customContainerClass="mt-2 px-0"
childrenContainerClass="flex-col gap-3 p-4"
@@ -180,7 +178,7 @@ export function AdvancedChartBuilder({
}
}}
htmlId="chart-time-dimension-toggle"
title={t("environments.analysis.charts.time_dimension")}
title={t("environments.analysis.charts.time_dimension_title")}
description={t("environments.analysis.charts.time_dimension_toggle_description")}
customContainerClass="mt-2 px-0"
childrenContainerClass="flex-col gap-3 p-4"
@@ -213,7 +211,7 @@ export function AdvancedChartBuilder({
}
}}
htmlId="chart-filters-toggle"
title={t("environments.analysis.charts.filters")}
title={t("environments.analysis.charts.filter_data")}
description={t("environments.analysis.charts.filters_toggle_description")}
customContainerClass="mt-2 px-0"
childrenContainerClass="flex-col gap-3 p-4"
@@ -227,9 +225,11 @@ export function AdvancedChartBuilder({
/>
</AdvancedOptionToggle>
<Button onClick={handleRunQuery} disabled={isLoading || !hasConfigChanged}>
{isLoading ? <LoadingSpinner /> : t("environments.analysis.charts.generate_chart")}
</Button>
<div className="flex justify-end">
<Button onClick={handleRunQuery} disabled={isLoading || !hasConfigChanged}>
{isLoading ? <LoadingSpinner /> : t("environments.analysis.charts.create_chart")}
</Button>
</div>
</div>
{!hidePreview && (
@@ -1,6 +1,6 @@
"use client";
import { ActivityIcon } from "lucide-react";
import { ActivityIcon, WandSparklesIcon } from "lucide-react";
import { useState } from "react";
import { toast } from "react-hot-toast";
import { useTranslation } from "react-i18next";
@@ -47,36 +47,34 @@ export function AIQuerySection({ environmentId, onChartGenerated }: Readonly<AIQ
return (
<div className="space-y-4 rounded-lg border border-gray-200 bg-white p-6 shadow-sm">
<div className="mb-4 flex items-center gap-2">
<div className="flex flex-col items-center gap-2 text-center">
<div className="bg-brand-dark/10 flex h-8 w-8 items-center justify-center rounded-full">
<ActivityIcon className="text-brand-dark h-5 w-5" />
</div>
<div>
<h2 className="font-semibold text-gray-900">
{t("environments.analysis.charts.ai_query_section_title")}
</h2>
<p className="text-sm text-gray-500">
{t("environments.analysis.charts.ai_query_section_description")}
</p>
</div>
<h2 className="font-semibold text-gray-900">
{t("environments.analysis.charts.ai_query_section_title")}
</h2>
<p className="text-sm text-gray-500">
{t("environments.analysis.charts.ai_query_section_description")}
</p>
</div>
<form className="flex gap-4" onSubmit={handleSubmit}>
<form className="flex flex-col gap-3" onSubmit={handleSubmit}>
<Input
autoFocus
placeholder={t("environments.analysis.charts.ai_query_placeholder")}
value={userQuery}
onChange={(e) => setUserQuery(e.target.value)}
className="flex-1"
maxLength={2000}
disabled={isGenerating}
/>
<Button
type="submit"
variant="default"
disabled={!userQuery.trim() || isGenerating}
loading={isGenerating}
className="bg-brand-dark hover:bg-brand-dark/90">
{t("common.generate")}
loading={isGenerating}>
<WandSparklesIcon className="h-4 w-4" />
{t("environments.analysis.charts.create_chart_with_ai")}
</Button>
</form>
</div>
@@ -1,105 +0,0 @@
"use client";
import { HelpCircle } from "lucide-react";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { Button } from "@/modules/ui/components/button";
import { Dialog, DialogBody, DialogContent, DialogHeader, DialogTitle } from "@/modules/ui/components/dialog";
interface ChartBuilderGuideProps {
/** Optional trigger; when not provided, caller renders their own */
trigger?: React.ReactNode;
}
export function ChartBuilderGuide({ trigger }: Readonly<ChartBuilderGuideProps>) {
const [isOpen, setIsOpen] = useState(false);
const { t } = useTranslation();
return (
<>
{trigger ?? (
<Button type="button" variant="ghost" size="sm" onClick={() => setIsOpen(true)}>
<HelpCircle className="mr-2 h-4 w-4" />
{t("environments.analysis.charts.guide_button")}
</Button>
)}
<Dialog open={isOpen} onOpenChange={(isOpen) => !isOpen && setIsOpen(false)}>
<DialogContent width="wide" className="max-h-[85vh] overflow-y-auto">
<DialogHeader>
<DialogTitle>{t("environments.analysis.charts.guide_title")}</DialogTitle>
</DialogHeader>
<DialogBody>
<div className="space-y-6">
<section>
<h3 className="text-md mb-1 font-semibold text-gray-900">
{t("environments.analysis.charts.guide_chart_type")}
</h3>
<p className="text-gray-600">{t("environments.analysis.charts.guide_chart_type_desc")}</p>
</section>
<section>
<h3 className="text-md mb-1 font-semibold text-gray-900">
{t("environments.analysis.charts.guide_measures")}
</h3>
<p className="text-sm text-gray-600">
{t("environments.analysis.charts.guide_measures_predefined")}
</p>
</section>
<section>
<h3 className="text-md mb-1 font-semibold text-gray-900">
{t("environments.analysis.charts.guide_dimensions")}
</h3>
<p className="text-sm text-gray-600">
{t("environments.analysis.charts.guide_dimensions_desc")}
</p>
</section>
<section>
<h3 className="text-md mb-1 font-semibold text-gray-900">
{t("environments.analysis.charts.guide_time_dimension")}
</h3>
<p className="text-sm text-gray-600">
{t("environments.analysis.charts.guide_time_dimension_desc")}
</p>
</section>
<section>
<h3 className="text-md mb-1 font-semibold text-gray-900">
{t("environments.analysis.charts.guide_filters")}
</h3>
<p className="text-sm text-gray-600">
{t("environments.analysis.charts.guide_filters_desc")}
</p>
</section>
<section>
<h3 className="text-md mb-2 font-semibold text-gray-900">
{t("environments.analysis.charts.guide_quick_ref")}
</h3>
<dl className="space-y-1.5 text-sm text-gray-600">
<div>
<dt className="inline font-medium text-gray-900">Measure: </dt>
<dd className="inline">{t("environments.analysis.charts.guide_term_measure")}</dd>
</div>
<div>
<dt className="inline font-medium text-gray-900">Dimension: </dt>
<dd className="inline">{t("environments.analysis.charts.guide_term_dimension")}</dd>
</div>
<div>
<dt className="inline font-medium text-gray-900">Time dimension: </dt>
<dd className="inline">{t("environments.analysis.charts.guide_term_time")}</dd>
</div>
<div>
<dt className="inline font-medium text-gray-900">Filter: </dt>
<dd className="inline">{t("environments.analysis.charts.guide_term_filter")}</dd>
</div>
</dl>
</section>
</div>
</DialogBody>
</DialogContent>
</Dialog>
</>
);
}
@@ -16,7 +16,7 @@ export function CreateChartButton({ environmentId }: Readonly<CreateChartButtonP
return (
<>
<Button onClick={() => setIsDialogOpen(true)}>
<Button size="sm" onClick={() => setIsDialogOpen(true)}>
<PlusIcon className="mr-2 h-4 w-4" />
{t("environments.analysis.charts.create_chart")}
</Button>
@@ -5,7 +5,6 @@ import { useTranslation } from "react-i18next";
import { AddToDashboardDialog } from "@/modules/ee/analysis/charts/components/add-to-dashboard-dialog";
import { AdvancedChartBuilder } from "@/modules/ee/analysis/charts/components/advanced-chart-builder";
import { AIQuerySection } from "@/modules/ee/analysis/charts/components/ai-query-section";
import { ChartBuilderGuide } from "@/modules/ee/analysis/charts/components/chart-builder-guide";
import { ChartDialogFooter } from "@/modules/ee/analysis/charts/components/chart-dialog-footer";
import { ChartPreview } from "@/modules/ee/analysis/charts/components/chart-preview";
import { ManualChartBuilder } from "@/modules/ee/analysis/charts/components/manual-chart-builder";
@@ -65,7 +64,7 @@ export function CreateChartView({
return (
<Dialog open={open} onOpenChange={(isOpen) => !isOpen && handleClose()}>
<DialogContent className="max-h-[90vh] overflow-y-auto" width="wide">
<DialogContent className="max-h-[90vh] overflow-y-auto" width="wide" disableCloseOnOutsideClick>
<DialogHeader>
<DialogTitle>{t("environments.analysis.charts.create_chart")}</DialogTitle>
<DialogDescription>{t("environments.analysis.charts.create_chart_description")}</DialogDescription>
@@ -79,19 +78,16 @@ export function CreateChartView({
<div className="w-full border-t border-gray-200" />
</div>
<div className="relative flex justify-center">
<span className="bg-gray-50 px-2 text-sm text-gray-500">
<span className="bg-white px-2 text-sm text-gray-500">
{t("environments.analysis.charts.OR")}
</span>
</div>
</div>
<div className="space-y-2">
<ChartBuilderGuide />
<ManualChartBuilder
selectedChartType={selectedChartType}
onChartTypeSelect={handleChartTypeChange}
/>
</div>
<ManualChartBuilder
selectedChartType={selectedChartType}
onChartTypeSelect={handleChartTypeChange}
/>
{selectedChartType && (
<AdvancedChartBuilder
@@ -2,6 +2,7 @@
import { useTranslation } from "react-i18next";
import { FEEDBACK_FIELDS, getTranslatedFieldLabel } from "@/modules/ee/analysis/lib/schema-definition";
import { Alert, AlertTitle } from "@/modules/ui/components/alert";
import { Label } from "@/modules/ui/components/label";
import { MultiSelect } from "@/modules/ui/components/multi-select";
@@ -38,7 +39,9 @@ export function DimensionsPanel({
onChange={onDimensionsChange}
placeholder={t("environments.analysis.charts.select_dimensions")}
/>
<p className="text-sm text-gray-500">{t("environments.analysis.charts.group_by_description")}</p>
<Alert variant="info" size="small">
<AlertTitle>{t("environments.analysis.charts.group_by_description")}</AlertTitle>
</Alert>
</div>
</div>
);
@@ -3,7 +3,6 @@
import { useTranslation } from "react-i18next";
import { AddToDashboardDialog } from "@/modules/ee/analysis/charts/components/add-to-dashboard-dialog";
import { AdvancedChartBuilder } from "@/modules/ee/analysis/charts/components/advanced-chart-builder";
import { ChartBuilderGuide } from "@/modules/ee/analysis/charts/components/chart-builder-guide";
import { ChartDialogFooter } from "@/modules/ee/analysis/charts/components/chart-dialog-footer";
import { ChartDialogLoadingView } from "@/modules/ee/analysis/charts/components/chart-dialog-loading-view";
import { ChartPreview } from "@/modules/ee/analysis/charts/components/chart-preview";
@@ -111,7 +110,6 @@ export function EditChartView({
/>
</div>
<div className="space-y-2">
<ChartBuilderGuide />
<ManualChartBuilder selectedChartType={chartType} onChartTypeSelect={handleChartTypeChange} />
</div>
<AdvancedChartBuilder
@@ -113,7 +113,7 @@ export function FiltersPanel({
return (
<div className="w-full space-y-2">
{hasMultipleFilters && (
<div className={`flex items-center ${hideTitle ? "justify-end" : "justify-between"}`}>
<div className={`flex items-center ${hideTitle ? "justify-start" : "justify-between"}`}>
{!hideTitle && (
<h3 className="text-md font-semibold text-gray-900">
{t("environments.analysis.charts.filters")}
@@ -124,7 +124,7 @@ export function FiltersPanel({
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="and">{t("common.and")}</SelectItem>
<SelectItem value="and">{t("environments.analysis.charts.and_filter_logic")}</SelectItem>
<SelectItem value="or">{t("environments.analysis.charts.or_filter_logic")}</SelectItem>
</SelectContent>
</Select>
@@ -19,7 +19,7 @@ export function ManualChartBuilder({
return (
<div className="space-y-2">
<div className="rounded-lg border border-gray-200 bg-white p-4">
<div className="grid grid-cols-2 gap-4 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6">
<div className="grid grid-cols-5 gap-4">
{chartTypes.map((chart) => {
const isSelected = selectedChartType === chart.id;
return (
@@ -59,7 +59,7 @@ export const CreateDashboardButton = ({ environmentId }: Readonly<CreateDashboar
return (
<>
<Button onClick={() => handleOpenChange(true)}>
<Button size="sm" onClick={() => handleOpenChange(true)}>
<PlusIcon className="mr-2 h-4 w-4" />
{t("environments.analysis.dashboards.create_dashboard")}
</Button>