From 9ac9df51e845215c219140b486ef4f5e17ec8c80 Mon Sep 17 00:00:00 2001 From: Ali Kamali Date: Tue, 17 Jun 2025 15:12:50 +0330 Subject: [PATCH 1/6] feat: improve details modal keyboard ux --- src/components/ComponentDetail/ButtonGrid.tsx | 158 +++++++++++++----- .../ComponentDetail/EditableButton.tsx | 62 +++---- src/components/ComponentDetail/FormFields.tsx | 48 +++--- src/components/ComponentDetail/index.tsx | 19 ++- src/types/ComponentDetailForm.ts | 2 + 5 files changed, 184 insertions(+), 105 deletions(-) diff --git a/src/components/ComponentDetail/ButtonGrid.tsx b/src/components/ComponentDetail/ButtonGrid.tsx index 5ef54e9..abd8590 100644 --- a/src/components/ComponentDetail/ButtonGrid.tsx +++ b/src/components/ComponentDetail/ButtonGrid.tsx @@ -1,22 +1,29 @@ -import { Plus } from "lucide-react"; +import { Plus, Grid, MessageSquare, Reply } from "lucide-react"; import EditableButton from "./EditableButton"; -import { GridItem } from "../../types/ComponentDetailForm"; +import { GridItem, KeyboardType } from "../../types/ComponentDetailForm"; import { generateUUID } from "./generateUUID"; type ButtonGridProps = { rows: GridItem[][]; setRows: React.Dispatch>; + keyboardType: KeyboardType; + setKeyboardType: React.Dispatch>; }; -export default function ButtonGrid({ rows, setRows }: ButtonGridProps) { - const MAX_ROWS = 5; - const MAX_COLS = 4; +export default function ButtonGrid({ + rows, + setRows, + keyboardType, + setKeyboardType, +}: ButtonGridProps) { + const MAX_ROWS = 3; + const MAX_COLS = 3; const addItemToRow = (rowIndex: number) => { setRows((prev) => prev.map((row, idx) => idx === rowIndex && row.length < MAX_COLS ? - [...row, { id: generateUUID(), label: "Item" }] + [...row, { id: generateUUID(), label: "New Button" }] : row, ), ); @@ -25,52 +32,115 @@ export default function ButtonGrid({ rows, setRows }: ButtonGridProps) { const addRow = () => { setRows((prev) => { if (prev.length >= MAX_ROWS) return prev; - const lastRow = prev[prev.length - 1]; - if (lastRow.length === 0) return prev; - return [...prev, [{ id: generateUUID(), label: "Item" }]]; + return [...prev, [{ id: generateUUID(), label: "New Button" }]]; }); }; return ( -
- {rows.map((row, rowIndex) => ( -
- {row.map((item, itemIndex) => { - return ( -
-
- +
+ {rows.length === 0 || rows.every((row) => row.length === 0) ? +
+ +

+ No Buttons Yet +

+

+ Start by adding your first button to create an interactive grid +

+ +
+ :
+ {/* Keyboard Type Toggle - Compact Version */} +
+ + Keyboard Type: + + + {/* Segmented Control */} +
+ + +
+
+ +
+ {rows.map((row, rowIndex) => ( +
+
+ {row.map((item, itemIndex) => ( +
+
+ +
+
+ ))}
+ +
- ); - })} + ))} - {row.length < MAX_COLS && ( - - )} + {rows.length < MAX_ROWS && ( + + )} +
- ))} - - {rows.length < MAX_ROWS && rows[rows.length - 1].length > 0 && ( - - )} + }
); } diff --git a/src/components/ComponentDetail/EditableButton.tsx b/src/components/ComponentDetail/EditableButton.tsx index 680105f..8e0bd82 100644 --- a/src/components/ComponentDetail/EditableButton.tsx +++ b/src/components/ComponentDetail/EditableButton.tsx @@ -1,4 +1,4 @@ -import { X } from "lucide-react"; +import { X, Pencil } from "lucide-react"; import { useState } from "react"; import { GridItem } from "../../types/ComponentDetailForm"; @@ -18,41 +18,28 @@ export default function EditableButton({ const [editingItemId, setEditingItemId] = useState(null); const [editingLabel, setEditingLabel] = useState(""); - function trimRows(newRows: GridItem[][], rowIndex: number): GridItem[][] { - if ( - newRows[rowIndex].length === 0 && - rowIndex + 1 < newRows.length && - newRows[rowIndex + 1].length > 0 - ) { - newRows[rowIndex].push(newRows[rowIndex + 1].shift()!); - } else { - return newRows; - } - - return trimRows(newRows, rowIndex + 1); - } - - const saveEdit = (itemId: string) => { - setRows((prev) => - prev.map((row) => + const saveEdit = (id: string) => { + setRows((prev) => { + const newRows = prev.map((row) => row.map((item) => - item.id === itemId ? { ...item, label: editingLabel } : item, + item.id === id ? { ...item, label: editingLabel } : item, ), - ), - ); + ); + return newRows; + }); setEditingItemId(null); - setEditingLabel(""); }; const removeItem = (rowIndex: number, itemIndex: number) => { setRows((prev) => { - let newRows = prev.map((row) => [...row]); - newRows[rowIndex].splice(itemIndex, 1); - - newRows = trimRows(newRows, rowIndex); + const newRows = [...prev]; + newRows[rowIndex] = newRows[rowIndex].filter( + (_, idx) => idx !== itemIndex, + ); - if (newRows[newRows.length - 1].length === 0 && newRows.length > 1) { - newRows.pop(); + // Remove empty rows + if (newRows[rowIndex].length === 0) { + newRows.splice(rowIndex, 1); } return newRows; @@ -60,7 +47,7 @@ export default function EditableButton({ }; return ( - <> +
{editingItemId === item.id ? setEditingLabel( - e.target.value.length > 0 ? e.target.value : "default", + e.target.value.length > 0 ? e.target.value : "New Button", ) } onBlur={() => saveEdit(item.id)} onKeyDown={(e) => { if (e.key === "Enter") saveEdit(item.id); }} - className="mx-auto my-auto text-center outline-none input-primary" + className="w-full rounded-lg border border-white/20 bg-white/10 px-4 py-2 text-center text-white placeholder-white/50 backdrop-blur-sm transition-all duration-200 outline-none focus:border-white/40 focus:ring-2 focus:ring-white/20" + placeholder="Enter button text..." /> :
{ setEditingItemId(item.id); setEditingLabel(item.label); }} > - {item.label} + {item.label} +
} - +
); } diff --git a/src/components/ComponentDetail/FormFields.tsx b/src/components/ComponentDetail/FormFields.tsx index b065d4e..7268e48 100644 --- a/src/components/ComponentDetail/FormFields.tsx +++ b/src/components/ComponentDetail/FormFields.tsx @@ -11,9 +11,14 @@ import { import { componentSchemaType } from "./makeFormData"; import { validateField } from "./validateField"; import { parseRawValue } from "./parseRawValue"; -import { formValuesType } from "../../types/ComponentDetailForm"; +import { + formValuesType, + GridItem, + KeyboardType, +} from "../../types/ComponentDetailForm"; import { SchemaType } from "../../types/Component"; import { useMemo, useState } from "react"; +import ButtonGrid from "./ButtonGrid"; type FormFieldsProps = { componentSchema: componentSchemaType; @@ -30,6 +35,9 @@ export default function FormFields({ setFormValues, setFormErrors, }: FormFieldsProps) { + const [rows, setRows] = useState([]); + const [keyboardType, setKeyboardType] = useState("inline"); + const handleChange = (key: string, value: File | string | null) => { setFormValues((prev) => ({ ...prev, [key]: value })); setFormErrors((prev) => ({ ...prev, [key]: "" })); @@ -184,24 +192,26 @@ export default function FormFields({ })} {canCollapse && ( - + <> + + )}
); diff --git a/src/components/ComponentDetail/index.tsx b/src/components/ComponentDetail/index.tsx index 72edf76..3938a37 100644 --- a/src/components/ComponentDetail/index.tsx +++ b/src/components/ComponentDetail/index.tsx @@ -6,7 +6,11 @@ import { import { useEffect, useState } from "react"; import Loading from "../Loading"; import api from "../../services/api"; -import { formValuesType, GridItem } from "../../types/ComponentDetailForm"; +import { + formValuesType, + GridItem, + KeyboardType, +} from "../../types/ComponentDetailForm"; import { toast } from "react-toastify"; import CodeEditor from "./CodeEditor"; import ButtonGrid from "./ButtonGrid"; @@ -15,7 +19,6 @@ import FormFields from "./FormFields"; import { useReactFlow } from "reactflow"; import { updateNodeHoverText } from "./updateNodeHoverText"; import { Check, RefreshCcw, X } from "lucide-react"; -import { generateUUID } from "./generateUUID"; type PropsType = { node: ComponentType; @@ -28,9 +31,8 @@ const ComponentDetail = ({ node, onClose }: PropsType) => { const [loading, setLoading] = useState(false); const [formValues, setFormValues] = useState({}); const [formErrors, setFormErrors] = useState>({}); - const [rows, setRows] = useState([ - [{ id: generateUUID(), label: "Item" }], - ]); + const [rows, setRows] = useState([]); + const [keyboardType, setKeyboardType] = useState("inline"); const { contentTypes } = useContentTypes(); const contentType = contentTypes!.find( @@ -135,7 +137,12 @@ const ComponentDetail = ({ node, onClose }: PropsType) => { setFormErrors={setFormErrors} /> - + }
diff --git a/src/types/ComponentDetailForm.ts b/src/types/ComponentDetailForm.ts index b0abff1..eea78f5 100644 --- a/src/types/ComponentDetailForm.ts +++ b/src/types/ComponentDetailForm.ts @@ -1,5 +1,7 @@ export type formValuesType = Record; +export type KeyboardType = "inline" | "reply"; + export type GridItem = { id: string; label: string; From a1b9b790362cbbbaa3e318b988471383307fe84d Mon Sep 17 00:00:00 2001 From: Ali Kamali Date: Tue, 17 Jun 2025 17:23:46 +0330 Subject: [PATCH 2/6] feat: add telegram preview --- src/components/ComponentDetail/ButtonGrid.tsx | 79 +-- .../ComponentDetail/TelegramPreview.tsx | 525 ++++++++++++++++++ src/components/ComponentDetail/index.tsx | 77 ++- 3 files changed, 648 insertions(+), 33 deletions(-) create mode 100644 src/components/ComponentDetail/TelegramPreview.tsx diff --git a/src/components/ComponentDetail/ButtonGrid.tsx b/src/components/ComponentDetail/ButtonGrid.tsx index abd8590..dac7240 100644 --- a/src/components/ComponentDetail/ButtonGrid.tsx +++ b/src/components/ComponentDetail/ButtonGrid.tsx @@ -1,13 +1,23 @@ -import { Plus, Grid, MessageSquare, Reply } from "lucide-react"; +import { Plus, Grid, MessageSquare, Reply, Eye } from "lucide-react"; import EditableButton from "./EditableButton"; -import { GridItem, KeyboardType } from "../../types/ComponentDetailForm"; +import TelegramPreview from "./TelegramPreview"; +import { + GridItem, + KeyboardType, + formValuesType, +} from "../../types/ComponentDetailForm"; import { generateUUID } from "./generateUUID"; +import { useState } from "react"; +import { componentSchemaType } from "./makeFormData"; type ButtonGridProps = { rows: GridItem[][]; setRows: React.Dispatch>; keyboardType: KeyboardType; setKeyboardType: React.Dispatch>; + formValues: formValuesType; + componentSchema: componentSchemaType; + componentName: string; }; export default function ButtonGrid({ @@ -15,6 +25,9 @@ export default function ButtonGrid({ setRows, keyboardType, setKeyboardType, + formValues, + componentSchema, + componentName, }: ButtonGridProps) { const MAX_ROWS = 3; const MAX_COLS = 3; @@ -57,38 +70,40 @@ export default function ButtonGrid({
:
- {/* Keyboard Type Toggle - Compact Version */} + {/* Header with Type Toggle */}
- - Keyboard Type: - +
+ + Keyboard Type: + - {/* Segmented Control */} -
- - + {/* Segmented Control */} +
+ + +
diff --git a/src/components/ComponentDetail/TelegramPreview.tsx b/src/components/ComponentDetail/TelegramPreview.tsx new file mode 100644 index 0000000..4c07994 --- /dev/null +++ b/src/components/ComponentDetail/TelegramPreview.tsx @@ -0,0 +1,525 @@ +import { + GridItem, + KeyboardType, + formValuesType, +} from "../../types/ComponentDetailForm"; +import { + Send, + MessageCircle, + FileText, + Image, + CheckCircle, + XCircle, + Video, + Music, + FileArchive, + FileCode, + FileSpreadsheet, + Download, + Phone, + MoreVertical, + Search, +} from "lucide-react"; +import { componentSchemaType } from "./makeFormData"; + +type TelegramPreviewProps = { + rows: GridItem[][]; + keyboardType: KeyboardType; + formValues: formValuesType; + componentSchema: componentSchemaType; + componentName: string; +}; + +export default function TelegramPreview({ + rows, + keyboardType, + formValues, + componentSchema, + componentName, +}: TelegramPreviewProps) { + const hasButtons = rows.length > 0 && rows.some((row) => row.length > 0); + + // Helper function to get file type and appropriate icon + const getFileTypeInfo = (fileName: string) => { + const ext = fileName.toLowerCase().split(".").pop() || ""; + + if (["jpg", "jpeg", "png", "gif", "webp", "svg", "bmp"].includes(ext)) { + return { type: "image", icon: Image, color: "text-green-400" }; + } + if (["mp4", "avi", "mov", "wmv", "flv", "webm", "mkv"].includes(ext)) { + return { type: "video", icon: Video, color: "text-red-400" }; + } + if (["mp3", "wav", "flac", "aac", "ogg", "m4a"].includes(ext)) { + return { type: "audio", icon: Music, color: "text-purple-400" }; + } + if (["zip", "rar", "7z", "tar", "gz", "bz2"].includes(ext)) { + return { type: "archive", icon: FileArchive, color: "text-orange-400" }; + } + if ( + [ + "js", + "ts", + "jsx", + "tsx", + "html", + "css", + "py", + "java", + "cpp", + "c", + "php", + "rb", + "go", + "rs", + ].includes(ext) + ) { + return { type: "code", icon: FileCode, color: "text-blue-400" }; + } + if (["xlsx", "xls", "csv", "ods"].includes(ext)) { + return { + type: "spreadsheet", + icon: FileSpreadsheet, + color: "text-green-400", + }; + } + if (["pdf", "doc", "docx", "txt", "rtf", "odt"].includes(ext)) { + return { type: "document", icon: FileText, color: "text-blue-400" }; + } + + return { type: "other", icon: Download, color: "text-gray-400" }; + }; + + const renderFieldValue = ( + key: string, + value: unknown, + schema: { type: string; verbose_name?: string }, + ) => { + if (!value && value !== false) return null; + + switch (schema.type) { + case "BooleanField": + return ( +
+ {value === "true" ? + <> + + Yes + + : <> + + No + + } +
+ ); + + case "FileField": + if (typeof value === "string") { + const fileName = value.split("/").pop() || "file"; + const fileInfo = getFileTypeInfo(fileName); + const IconComponent = fileInfo.icon; + + if (fileInfo.type === "image") { + return ( +
+ {fileName} +
+ ); + } else if (fileInfo.type === "video") { + return ( +
+ +
+ ); + } else if (fileInfo.type === "audio") { + return ( +
+
+
+ +
+
+

+ {fileName} +

+

+ Audio file +

+
+
+ +
+ ); + } else { + return ( +
+
+ +
+
+

+ {fileName} +

+

+ Click to download • {fileInfo.type} +

+
+
+ ); + } + } else if ( + typeof value === "object" && + value !== null && + "name" in value + ) { + // Handle File object (when file is uploaded but not yet saved) + const fileName = (value as { name: string }).name; + const fileInfo = getFileTypeInfo(fileName); + const IconComponent = fileInfo.icon; + + if (fileInfo.type === "image" && value instanceof File) { + const imageUrl = URL.createObjectURL(value); + return ( +
+ {fileName} +
+ ); + } else if (fileInfo.type === "video" && value instanceof File) { + const videoUrl = URL.createObjectURL(value); + return ( +
+ +
+ ); + } else if (fileInfo.type === "audio" && value instanceof File) { + const audioUrl = URL.createObjectURL(value); + return ( +
+
+
+ +
+
+

+ {fileName} +

+

+ Audio file +

+
+
+ +
+ ); + } else { + return ( +
+
+ +
+
+

+ {fileName} +

+

+ Ready to upload • {fileInfo.type} +

+
+
+ ); + } + } + return null; + + default: + return ( +
+ {typeof value === "string" ? + value + : typeof value === "number" ? + String(value) + : typeof value === "boolean" ? + String(value) + : ""} +
+ ); + } + }; + + const getComponentContent = () => { + // Fields that should be displayed in Telegram preview + const displayableFields = [ + "text", + "caption", + "message", + "content", + "description", + ]; + + const fields = Object.entries(componentSchema) + .filter(([key, schema]) => { + const value = formValues[key]; + if (!value && value !== false) return false; + + // Skip internal/technical fields + const internalFields = [ + "chat_id", + "user_id", + "message_id", + "thread_id", + "reply_to_message_id", + "bot_token", + "webhook_url", + ]; + if (internalFields.includes(key.toLowerCase())) return false; + + // Include file fields + if (schema.type === "FileField") return true; + + // Include boolean fields that are user-facing + if (schema.type === "BooleanField") return true; + + // Include displayable text fields + if ( + displayableFields.some((field) => key.toLowerCase().includes(field)) + ) + return true; + + // Include other text fields that aren't internal + if (typeof value === "string" && value.trim() !== "") return true; + + return false; + }) + .map(([key, schema]) => ({ + key, + schema, + value: formValues[key], + label: schema.verbose_name || key, + })); + + return fields; + }; + + const componentFields = getComponentContent(); + + return ( +
+ {/* Telegram Header */} +
+
+
+
+ +
+
+

+ Bot Preview +

+
+
+

+ online +

+
+
+
+ + + +
+
+
+ + {/* Chat Area */} +
+ {/* Chat Pattern Overlay */} +
+ + {/* Bot Message with Component Data */} +
+
+
+
+
+
+
+
+ {/* Message tail */} +
+
+
+ + {/* Component Title */} + {componentFields.length > 0 && ( +
+
+
+ 📋 +
+

+ {componentName} +

+
+
+ )} + + {/* Component Fields */} + {componentFields.length > 0 ? +
+ {componentFields.map(({ key, schema, value }) => ( +
{renderFieldValue(key, value, schema)}
+ ))} +
+ :
+
+ +
+

+ Fill in the form fields to see them here +

+
+ } + + {/* Inline Keyboard */} + {keyboardType === "inline" && hasButtons && ( +
+
+ {rows.map((row, rowIndex) => ( +
+ {row.map((button) => ( + + ))} +
+ ))} +
+
+ )} +
+
+

Bot

+
+

just now

+
+
+ + + + +
+
+
+
+
+ +
+ {/* Input Area */} +
+
+ + + +
+ +
+ + {/* Reply Keyboard */} + {keyboardType === "reply" && hasButtons && ( +
+
+ {rows.map((row, rowIndex) => ( +
+ {row.map((button) => ( + + ))} +
+ ))} +
+
+ )} +
+
+
+ ); +} diff --git a/src/components/ComponentDetail/index.tsx b/src/components/ComponentDetail/index.tsx index 3938a37..94f0e38 100644 --- a/src/components/ComponentDetail/index.tsx +++ b/src/components/ComponentDetail/index.tsx @@ -18,7 +18,8 @@ import { makeFormData } from "./makeFormData"; import FormFields from "./FormFields"; import { useReactFlow } from "reactflow"; import { updateNodeHoverText } from "./updateNodeHoverText"; -import { Check, RefreshCcw, X } from "lucide-react"; +import { Check, RefreshCcw, X, Eye } from "lucide-react"; +import TelegramPreview from "./TelegramPreview"; type PropsType = { node: ComponentType; @@ -33,6 +34,7 @@ const ComponentDetail = ({ node, onClose }: PropsType) => { const [formErrors, setFormErrors] = useState>({}); const [rows, setRows] = useState([]); const [keyboardType, setKeyboardType] = useState("inline"); + const [showPreview, setShowPreview] = useState(false); const { contentTypes } = useContentTypes(); const contentType = contentTypes!.find( @@ -50,6 +52,26 @@ const ComponentDetail = ({ node, onClose }: PropsType) => { setFormValues(details ?? {}); }, [details]); + // Check if all required fields are filled + const canShowPreview = () => { + // Check if all required fields are filled + const requiredFields = Object.entries(componentSchema) + .filter(([_, schema]) => schema.required) + .map(([key, _]) => key); + + // If no required fields, show preview + if (requiredFields.length === 0) return true; + + const allRequiredFilled = requiredFields.every((field) => { + const value = formValues[field]; + return ( + value !== undefined && value !== null && value !== "" && value !== false + ); + }); + + return allRequiredFilled; + }; + const handleSubmit = (override?: formValuesType) => { const formData = makeFormData(componentSchema, override, formValues); @@ -94,6 +116,19 @@ const ComponentDetail = ({ node, onClose }: PropsType) => {
{node.id}
+ + {/* Preview Button */} + {canShowPreview() && ( + + )} +
} + + {/* Telegram Preview Modal */} + {showPreview && canShowPreview() && ( +
+
+
+

📱 Telegram Preview

+

+ How your component will look in Telegram +

+
+
+ +
+
+ +
+
+
setShowPreview(false)} + >
+
+ )} +
From 875c671ff3b1bae494022f6374ad780d64f20c94 Mon Sep 17 00:00:00 2001 From: arminfatahi Date: Tue, 17 Jun 2025 19:46:51 +0330 Subject: [PATCH 3/6] fix build --- src/components/ComponentDetail/ButtonGrid.tsx | 7 +------ src/components/ComponentDetail/FormFields.tsx | 10 +--------- src/components/ComponentDetail/TelegramPreview.tsx | 2 +- 3 files changed, 3 insertions(+), 16 deletions(-) diff --git a/src/components/ComponentDetail/ButtonGrid.tsx b/src/components/ComponentDetail/ButtonGrid.tsx index dac7240..f4d2f3a 100644 --- a/src/components/ComponentDetail/ButtonGrid.tsx +++ b/src/components/ComponentDetail/ButtonGrid.tsx @@ -1,13 +1,11 @@ -import { Plus, Grid, MessageSquare, Reply, Eye } from "lucide-react"; +import { Plus, Grid, MessageSquare, Reply } from "lucide-react"; import EditableButton from "./EditableButton"; -import TelegramPreview from "./TelegramPreview"; import { GridItem, KeyboardType, formValuesType, } from "../../types/ComponentDetailForm"; import { generateUUID } from "./generateUUID"; -import { useState } from "react"; import { componentSchemaType } from "./makeFormData"; type ButtonGridProps = { @@ -25,9 +23,6 @@ export default function ButtonGrid({ setRows, keyboardType, setKeyboardType, - formValues, - componentSchema, - componentName, }: ButtonGridProps) { const MAX_ROWS = 3; const MAX_COLS = 3; diff --git a/src/components/ComponentDetail/FormFields.tsx b/src/components/ComponentDetail/FormFields.tsx index 7268e48..24faf12 100644 --- a/src/components/ComponentDetail/FormFields.tsx +++ b/src/components/ComponentDetail/FormFields.tsx @@ -11,14 +11,9 @@ import { import { componentSchemaType } from "./makeFormData"; import { validateField } from "./validateField"; import { parseRawValue } from "./parseRawValue"; -import { - formValuesType, - GridItem, - KeyboardType, -} from "../../types/ComponentDetailForm"; +import { formValuesType } from "../../types/ComponentDetailForm"; import { SchemaType } from "../../types/Component"; import { useMemo, useState } from "react"; -import ButtonGrid from "./ButtonGrid"; type FormFieldsProps = { componentSchema: componentSchemaType; @@ -35,9 +30,6 @@ export default function FormFields({ setFormValues, setFormErrors, }: FormFieldsProps) { - const [rows, setRows] = useState([]); - const [keyboardType, setKeyboardType] = useState("inline"); - const handleChange = (key: string, value: File | string | null) => { setFormValues((prev) => ({ ...prev, [key]: value })); setFormErrors((prev) => ({ ...prev, [key]: "" })); diff --git a/src/components/ComponentDetail/TelegramPreview.tsx b/src/components/ComponentDetail/TelegramPreview.tsx index 4c07994..558520f 100644 --- a/src/components/ComponentDetail/TelegramPreview.tsx +++ b/src/components/ComponentDetail/TelegramPreview.tsx @@ -90,7 +90,7 @@ export default function TelegramPreview({ }; const renderFieldValue = ( - key: string, + _key: string, value: unknown, schema: { type: string; verbose_name?: string }, ) => { From ac19912f84ce5996c945c9c4858e70e622bc1fe4 Mon Sep 17 00:00:00 2001 From: arminfatahi Date: Tue, 17 Jun 2025 20:39:18 +0330 Subject: [PATCH 4/6] minor fix --- src/components/ComponentDetail/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ComponentDetail/index.tsx b/src/components/ComponentDetail/index.tsx index 94f0e38..05583be 100644 --- a/src/components/ComponentDetail/index.tsx +++ b/src/components/ComponentDetail/index.tsx @@ -118,7 +118,7 @@ const ComponentDetail = ({ node, onClose }: PropsType) => {
{/* Preview Button */} - {canShowPreview() && ( + {canShowPreview() && !isFetching && ( - + +
+
+

📱 Telegram Preview

+

+ How your component will look in Telegram +

+
+
+ +
+
+
-
setShowPreview(false)} - >
- )} +
modalRef.current?.close()} + >
+
From baaeb9f1c5b410b420d69da726e6cefcadb2c18d Mon Sep 17 00:00:00 2001 From: arminfatahi Date: Wed, 18 Jun 2025 03:35:21 +0330 Subject: [PATCH 6/6] minor fix --- src/components/ComponentDetail/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/ComponentDetail/index.tsx b/src/components/ComponentDetail/index.tsx index 03753a2..f934cbf 100644 --- a/src/components/ComponentDetail/index.tsx +++ b/src/components/ComponentDetail/index.tsx @@ -34,7 +34,6 @@ const ComponentDetail = ({ node, onClose }: PropsType) => { const [formErrors, setFormErrors] = useState>({}); const [rows, setRows] = useState([]); const [keyboardType, setKeyboardType] = useState("inline"); - const [showPreview, setShowPreview] = useState(false); const modalRef = useRef(null); const { contentTypes } = useContentTypes();