import React, { useEffect, useState } from "react"; import { Icon } from "../../tremor/Icon"; import { Input } from "../../tremor/Input"; import { Button } from "../../tremor/Button"; import { Widget, WidgetSettings } from "@onvo-ai/js"; import { create } from "zustand"; import { useDashboard } from "../../layouts/Dashboard/useDashboard"; import { useBackend } from "../../layouts/Wrapper"; import { Text, Label, Title } from "../../tremor/Text"; import { Editor, EditorProvider, BtnBold, BtnBulletList, BtnClearFormatting, BtnItalic, BtnLink, BtnNumberedList, BtnRedo, BtnStrikeThrough, BtnUnderline, BtnUndo, HtmlButton, Separator, Toolbar, } from "react-simple-wysiwyg"; import { twMerge } from "tailwind-merge"; import { ChevronLeftIcon, ChevronRightIcon, TrashIcon, } from "@heroicons/react/20/solid"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "../../tremor/Select"; import { Divider } from "../../tremor/Divider"; import ChartBase from "../Chart/ChartBase"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "../../tremor/Tabs"; import { Card } from "../../tremor/Card"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "../../tremor/Accordion"; import { Textarea } from "../../tremor/Textarea"; import { SparklesIcon } from "@heroicons/react/24/solid"; import { toast } from "sonner"; import { useMaxHeight } from "../../lib/useMaxHeight"; import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels"; export const useTextWidgetModal = create<{ open: boolean; widget?: Widget; setOpen: (open: boolean, widget?: Widget) => void; }>((set) => ({ open: false, setOpen: (op: boolean, wid?: Widget) => set({ open: op, widget: wid }), })); export const TextWidgetModal: React.FC<{}> = ({ }) => { const { dashboard, refreshWidgets, tab, widgets } = useDashboard(); const { backend, adminMode } = useBackend(); const { open, setOpen, widget } = useTextWidgetModal(); const { lg, sm } = useMaxHeight(); const [title, setTitle] = useState(""); const [subtitle, setSubtitle] = useState(""); const [prompt, setPrompt] = useState(""); const [summaryLoading, setSummaryLoading] = useState(false); const [loading, setLoading] = useState(false); const [titleAlignment, setTitleAlignment] = useState< "start" | "center" | "end" >("start"); const [descriptionAlignment, setDescriptionAlignment] = useState< "start" | "center" | "end" >("start"); const [settings, setSettings] = useState({}); const [activeTab, setActiveTab] = useState("configure"); const [drilldownWidget, setDrilldownWidget] = useState(null); const deleteWidget = async () => { if (!widget || !backend) return; toast.promise( async () => { await backend.widgets.delete(widget.id); refreshWidgets(backend); setOpen(false); }, { loading: "Deleting widget...", success: "Widget deleted", error: (err) => `Failed to delete widget: ${err.message}`, } ); }; useEffect(() => { if (widget) { const config = widget.config as any; setTitle(widget.title || ""); setSubtitle(config?.options?.plugins?.subtitle?.text || ""); setSettings(widget.settings || {}); setTitleAlignment( config?.options?.plugins?.title?.align || "start" ); setDescriptionAlignment( config?.options?.plugins?.subtitle?.align || "start" ); setPrompt(config?.options?.plugins?.text?.prompt || ""); setDrilldownWidget(widget.drilldown_widget); } else { setTitle(""); setSubtitle(""); setPrompt(""); setTitleAlignment("start"); setDescriptionAlignment("start"); setSettings({}); setDrilldownWidget(null); } }, [widget]); const createWidget = async () => { if (!dashboard || !backend) return; setLoading(true); let cache = { type: "text", data: { datasets: [{ data: [], label: "" }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { title: { display: true, text: title, align: titleAlignment }, text: { prompt: prompt, }, subtitle: { display: false, text: subtitle, align: descriptionAlignment, }, }, }, }; if (widget) { await backend.widgets.update(widget.id, { title: title, code: "", config: cache, settings: settings, drilldown_widget: drilldownWidget, }); } else { await backend.widgets.create({ dashboard: dashboard.id, layouts: { lg: { x: 0, y: lg, w: 12, h: 10, }, sm: { x: 0, y: sm, w: 3, h: 10, }, }, use_in_library: false, use_in_chat: false, title: title, team: dashboard.team, code: "", messages: [], settings: settings, error: null, tab: tab || 0, config: cache, engine: "manual-v1", type: "text", drilldown_widget: drilldownWidget, }); } setOpen(false); setLoading(false); setTitle(""); setSubtitle(""); refreshWidgets(backend); }; const summarize = async () => { if (!backend || !dashboard) return; setSummaryLoading(true); try { let data = await backend.dashboard(dashboard.id).summarize(prompt); setTitle(data.title); setSubtitle(data.description); setSummaryLoading(false); } catch (e: any) { toast.error("Failed to summarize: " + e.message); setSummaryLoading(false); } }; if (!dashboard?.settings?.can_create_widgets && !adminMode) return <>; return ( <>
setOpen(false)} />
{dashboard?.title}
setActiveTab(value)} className="onvo-flex onvo-flex-col onvo-h-full" >
Configure Settings
Title setTitle(e.target.value)} />
Title alignment { setTitleAlignment(val as "start" | "center" | "end"); }} > Left Center End
Description
setSubtitle(e.target.value)} >
Description alignment { setDescriptionAlignment( val as "start" | "center" | "end" ); }} > Left Center End
Generate text with AI