From d43b6cad51b0d1864448495c021356149b58029d Mon Sep 17 00:00:00 2001 From: akshaykumar2505 Date: Thu, 23 Apr 2026 00:19:40 +0530 Subject: [PATCH] feat: enhance Smart Generation with branch management and localization updates - Added new localization keys for Smart Generation in English, Russian, and Chinese, including output names and save location hints. - Updated Smart Generation component to manage branch selection more effectively, allowing users to save generated models to specific branches. - Introduced UI elements for branch alerts and output name sections to improve user experience. This update improves the overall functionality and usability of the Smart Generation feature, ensuring better integration with branch management. --- public/locales/en/models.json | 8 +- public/locales/ru/models.json | 8 +- public/locales/zh/models.json | 8 +- .../SmartGeneration/index.module.less | 15 ++ src/components/SmartGeneration/index.tsx | 130 +++++++++++++++--- src/pages/Models/index.tsx | 10 +- 6 files changed, 153 insertions(+), 26 deletions(-) diff --git a/public/locales/en/models.json b/public/locales/en/models.json index a639e20d..c7bde4a5 100644 --- a/public/locales/en/models.json +++ b/public/locales/en/models.json @@ -34,5 +34,11 @@ "remove_branch": "Remove branch" }, "last_modify": "Last modify", - "no_model": "Select model to edit" + "no_model": "Select model to edit", + "smart_gen": { + "output_names_title": "Output names & save location", + "output_save_branch_label": "Save generated model to branch", + "output_save_branch_hint": "The new file and version are created on this branch (can differ from the sidebar). By default, matches the branch you used when profiling; change it here before Preview or Apply.", + "no_branches": "This data source has no branches. Create a branch first, then use Smart Generate." + } } diff --git a/public/locales/ru/models.json b/public/locales/ru/models.json index c9f1bc10..99713dd6 100644 --- a/public/locales/ru/models.json +++ b/public/locales/ru/models.json @@ -34,5 +34,11 @@ "remove_branch": "Удалить ветку" }, "last_modify": "Последнее изменение", - "no_model": "Выберите модель для редактирования" + "no_model": "Выберите модель для редактирования", + "smart_gen": { + "output_names_title": "Имена и место сохранения", + "output_save_branch_label": "Сохранить сгенерированную модель в ветку", + "output_save_branch_hint": "Новый файл и версия создаются в этой ветке (может отличаться от боковой панели). По умолчанию совпадает с веткой, в которой вы запускали профилирование; перед предпросмотром или применением можно сменить.", + "no_branches": "У источника данных нет веток. Сначала создайте ветку, затем запустите умную генерацию." + } } diff --git a/public/locales/zh/models.json b/public/locales/zh/models.json index 8285aeea..6c9cfc20 100644 --- a/public/locales/zh/models.json +++ b/public/locales/zh/models.json @@ -34,5 +34,11 @@ "remove_branch": "删除分支" }, "last_modify": "上次修改", - "no_model": "选择要编辑的模型" + "no_model": "选择要编辑的模型", + "smart_gen": { + "output_names_title": "输出名称与保存位置", + "output_save_branch_label": "将生成的模型保存到分支", + "output_save_branch_hint": "新文件和版本会写入此分支(可与侧栏不同)。默认与剖析时所在分支一致;在预览或应用前可在此修改。", + "no_branches": "此数据源还没有分支。请先创建分支,再使用智能生成。" + } } diff --git a/src/components/SmartGeneration/index.module.less b/src/components/SmartGeneration/index.module.less index a5146db9..c5563d88 100644 --- a/src/components/SmartGeneration/index.module.less +++ b/src/components/SmartGeneration/index.module.less @@ -564,3 +564,18 @@ gap: 8px; margin-bottom: 8px; } + +.branchAlert { + margin-bottom: 16px; +} + +.outputNamesSection { + border: 1px solid rgba(114, 46, 209, 0.18); + background: #faf5ff; + border-radius: 8px; + padding: 16px 16px 12px; +} + +.saveBranchInOutput { + display: block; +} diff --git a/src/components/SmartGeneration/index.tsx b/src/components/SmartGeneration/index.tsx index 21c29b76..87d08f21 100644 --- a/src/components/SmartGeneration/index.tsx +++ b/src/components/SmartGeneration/index.tsx @@ -18,8 +18,9 @@ import { useTranslation } from "react-i18next"; import Button from "@/components/Button"; import AuthTokensStore from "@/stores/AuthTokensStore"; -import type { DataSourceInfo, Schema } from "@/types/dataSource"; +import type { Branch, DataSourceInfo, Schema } from "@/types/dataSource"; import { + Branch_Statuses_Enum, useSmartGenDataSchemasMutation, useFetchMetaQuery, } from "@/graphql/generated"; @@ -846,7 +847,8 @@ interface SmartGenerationProps { dataSource: DataSourceInfo; schema: Schema | undefined; branchId: string; - onComplete: () => void; + branches: Branch[]; + onComplete: (savedBranchId: string) => void; onCancel: () => void; /** Pre-selected table schema (e.g. "cst") from model provenance — skips table selection */ initialSchema?: string; @@ -860,6 +862,7 @@ const SmartGeneration: FC = ({ dataSource, schema, branchId, + branches, onComplete, onCancel, initialSchema, @@ -867,8 +870,12 @@ const SmartGeneration: FC = ({ previousFilters, }) => { const { t } = useTranslation(["models", "common"]); - const hasInitial = !!(initialSchema && initialTable); const [step, setStep] = useState("select"); + const [targetBranchId, setTargetBranchId] = useState(branchId); + const [lockedBranchId, setLockedBranchId] = useState(null); + const [saveToBranchId, setSaveToBranchId] = useState(""); + + const metaBranchId = lockedBranchId || targetBranchId; const [filters, setFilters] = useState( previousFilters || [] ); @@ -907,19 +914,34 @@ const SmartGeneration: FC = ({ const [smartGenResult, execSmartGen] = useSmartGenDataSchemasMutation(); + useEffect(() => { + setTargetBranchId(branchId); + }, [branchId]); + + useEffect(() => { + if (!branches?.length) return; + const ids = new Set(branches.map((b) => b.id)); + if (targetBranchId && ids.has(targetBranchId)) return; + if (branchId && ids.has(branchId)) { + setTargetBranchId(branchId); + return; + } + setTargetBranchId(branches[0].id); + }, [branches, branchId, targetBranchId]); + // Fetch Cube.js meta — same query the Explore page uses. // pause=false when both datasource + branch are available (mirrors Explore page pattern). const [metaResult, execMetaQuery] = useFetchMetaQuery({ - variables: { datasource_id: dataSource.id!, branch_id: branchId }, - pause: !dataSource.id || !branchId, + variables: { datasource_id: dataSource.id!, branch_id: metaBranchId }, + pause: !dataSource.id || !metaBranchId, }); // Re-fetch meta when datasource or branch changes useEffect(() => { - if (dataSource.id && branchId) { + if (dataSource.id && metaBranchId) { execMetaQuery(); } - }, [dataSource.id, branchId, execMetaQuery]); + }, [dataSource.id, metaBranchId, execMetaQuery]); // Build dimension map: raw ClickHouse column name → fully-qualified Cube.js // dimension member (e.g. "event" → "semantic_events.event"). @@ -1009,6 +1031,7 @@ const SmartGeneration: FC = ({ abortRef.current = controller; setStep("profiling"); + setLockedBranchId(targetBranchId); setError(null); setRawProfile(null); setChangePreview(null); @@ -1026,12 +1049,12 @@ const SmartGeneration: FC = ({ Accept: "text/event-stream", ...(token ? { Authorization: `Bearer ${token}` } : {}), "x-hasura-datasource-id": dataSource.id!, - "x-hasura-branch-id": branchId, + "x-hasura-branch-id": targetBranchId, }, body: JSON.stringify({ table: tblName, schema: tblSchema, - branchId, + branchId: targetBranchId, ...(filters.length > 0 ? { filters } : {}), }), signal: controller.signal, @@ -1088,10 +1111,13 @@ const SmartGeneration: FC = ({ .map((c: any) => c.name); setSelectedColumns(new Set(active)); } + setSaveToBranchId(targetBranchId); setStep("preview"); } else if (eventType === "error") { setError(data.error || "Profiling failed"); setStep("select"); + setLockedBranchId(null); + setSaveToBranchId(""); } }; @@ -1130,9 +1156,11 @@ const SmartGeneration: FC = ({ if (err.name === "AbortError") return; // cancelled by user setError(err.message || "Profiling failed"); setStep("select"); + setLockedBranchId(null); + setSaveToBranchId(""); }); }, - [dataSource.id, branchId, filters] + [dataSource.id, targetBranchId, filters] ); // Sync late-arriving initial selection (e.g. after page refresh). @@ -1177,6 +1205,20 @@ const SmartGeneration: FC = ({ return options; }, [schema]); + const branchOptions = useMemo( + () => + (branches || []).map((b) => ({ + value: b.id, + label: + b.status === Branch_Statuses_Enum.Active + ? `${b.name} (${t("common:words.default")})` + : b.name, + })), + [branches, t] + ); + + const saveBranch = saveToBranchId || lockedBranchId || targetBranchId; + const isTableOptionsLoading = !schema || tableOptions.length === 0; const selectedTableValue = useMemo(() => { @@ -1196,9 +1238,13 @@ const SmartGeneration: FC = ({ }, []); const handleProfile = useCallback(() => { + if (!branchOptions.length) { + setError(t("models:smart_gen.no_branches")); + return; + } if (!selectedTable || !selectedSchema) return; startProfile(selectedSchema, selectedTable); - }, [selectedTable, selectedSchema, startProfile]); + }, [branchOptions.length, selectedTable, selectedSchema, startProfile, t]); // Preview changes (dry-run) — no ClickHouse query, uses cached profile const handlePreviewChanges = useCallback(async () => { @@ -1216,7 +1262,7 @@ const SmartGeneration: FC = ({ const result = await execSmartGen({ datasource_id: dataSource.id!, - branch_id: branchId, + branch_id: saveBranch, table_name: selectedTable, table_schema: selectedSchema, merge_strategy: mergeStrategy, @@ -1282,7 +1328,7 @@ const SmartGeneration: FC = ({ setStep("change_preview"); }, [ dataSource.id, - branchId, + saveBranch, selectedTable, selectedSchema, mergeStrategy, @@ -1319,7 +1365,7 @@ const SmartGeneration: FC = ({ const result = await execSmartGen({ datasource_id: dataSource.id!, - branch_id: branchId, + branch_id: saveBranch, table_name: selectedTable, table_schema: selectedSchema, merge_strategy: mergeStrategy, @@ -1346,7 +1392,7 @@ const SmartGeneration: FC = ({ setStep("done"); }, [ dataSource.id, - branchId, + saveBranch, selectedTable, selectedSchema, mergeStrategy, @@ -1365,6 +1411,8 @@ const SmartGeneration: FC = ({ abortRef.current?.abort(); setError(null); setStep("select"); + setLockedBranchId(null); + setSaveToBranchId(""); }, []); const genData = smartGenResult.data?.smart_gen_dataschemas; @@ -1384,6 +1432,15 @@ const SmartGeneration: FC = ({ + {step === "select" && branchOptions.length === 0 && ( + + )} + {/* Step 1: Table Selection */} {step === "select" && ( <> @@ -1440,7 +1497,7 @@ const SmartGeneration: FC = ({ tableName={selectedTable} tableSchema={selectedSchema} datasourceId={dataSource.id!} - branchId={branchId} + branchId={metaBranchId} /> )} @@ -1461,7 +1518,7 @@ const SmartGeneration: FC = ({