mirror of
https://github.com/formbricks/formbricks.git
synced 2026-04-23 21:59:28 -05:00
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:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user