import React from "react"; import { Icon } from "../../tremor/Icon"; import { Input } from "../../tremor/Input"; import { Button } from "../../tremor/Button"; import { useEffect, useState } from "react"; import { create } from "zustand"; import { useDashboard } from "../../layouts/Dashboard/useDashboard"; import { Widget, WidgetSettings } from "@onvo-ai/js"; import { useBackend } from "../../layouts/Wrapper"; import { Text, Label, Title } from "../../tremor/Text"; import { twMerge } from "tailwind-merge"; import { ChevronLeftIcon, ChevronRightIcon, PaperClipIcon, TrashIcon } from "@heroicons/react/20/solid"; import ChartBase from "../Chart/ChartBase"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "../../tremor/Tabs"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "../../tremor/Select"; import { Divider } from "../../tremor/Divider"; import { Card } from "../../tremor/Card"; import { toast } from "sonner"; import { useMaxHeight } from "../../lib/useMaxHeight"; import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels"; export const useImageWidgetModal = 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 ImageWidgetModal: React.FC<{}> = ({ }) => { const { dashboard, refreshWidgets, tab, widgets } = useDashboard(); const { backend, adminMode } = useBackend(); const { open, setOpen, widget } = useImageWidgetModal(); const { lg, sm } = useMaxHeight(); const [url, setUrl] = useState(""); const [loading, setLoading] = useState(false); const [imageFill, setImageFill] = useState<"fit" | "fill">("fill"); 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; setUrl(config?.options?.plugins?.image?.url || ""); setImageFill(config?.options?.plugins?.image?.fill || "fill"); setSettings(widget.settings || {}); setDrilldownWidget(widget.drilldown_widget); } else { setImageFill("fill"); setUrl(""); setSettings({}); setDrilldownWidget(null); } }, [widget]); const createWidget = async () => { if (!dashboard || !backend) return; setLoading(true); let cache = { type: "image", data: { datasets: [{ data: [], label: "" }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { image: { url: url, fill: imageFill }, }, }, }; if (widget) { await backend.widgets.update(widget.id, { title: url, 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: url, team: dashboard.team, code: "", messages: [], error: null, settings: settings, tab: tab || 0, config: cache, engine: "manual-v1", type: "image", drilldown_widget: drilldownWidget, }); } setOpen(false); setLoading(false); setUrl(""); refreshWidgets(backend); }; const uploadFile = async (e: React.ChangeEvent) => { toast.promise( async () => { if (!e.target.files) return; return backend?.utils.uploadFile(e.target.files[0]); }, { loading: "Uploading file...", success: (res: any) => { setUrl(res.url); return "File uploaded"; }, error: (err: any) => { return "Failed to upload file: " + err.message; }, } ); }; 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
Image URL setUrl(e.target.value)} /> OR
Drag and drop an image here or click to add Upload .jpeg, .png, .svg files
Image fill { setImageFill(val as "fit" | "fill"); }} > Fit Fill
Widget ID
CSS ID setSettings({ ...settings, css_id: e.target.value }) } inputClassName="onvo-background-color onvo-border-black/10 dark:onvo-border-white/10" />
CSS classes setSettings({ ...settings, css_classnames: e.target.value, }) } inputClassName="onvo-background-color onvo-border-black/10 dark:onvo-border-white/10" />
Link setSettings({ ...settings, link: e.target.value }) } inputClassName="onvo-background-color onvo-border-black/10 dark:onvo-border-white/10" />
Drilldown widget
); };