From 9d39faf2cfe33948938e5be1cb6beed8a954bcb0 Mon Sep 17 00:00:00 2001 From: alphaeusmote <41258468-alphaeusmote@users.noreply.replit.com> Date: Thu, 10 Apr 2025 00:32:56 +0000 Subject: [PATCH] Enhance dashboard to display data for all Active Directory objects and dynamically generate available fields. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 705f2157-ef97-4fbd-89e4-8c7f2ecaea90 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/7ed01c5f-a82d-405a-b728-b2e3d127c60c/3caa578d-3a2c-45ec-a6c0-75313ad18bfb.jpg --- .../dashboard/share-dashboard-dialog.tsx | 421 ++++++++++++++++++ client/src/components/ui/color-picker.tsx | 120 +++++ client/src/lib/snapshot-utils.ts | 95 ++++ client/src/pages/dashboard-page.tsx | 140 +++--- server/routes.ts | 156 +++++++ 5 files changed, 874 insertions(+), 58 deletions(-) create mode 100644 client/src/components/dashboard/share-dashboard-dialog.tsx create mode 100644 client/src/components/ui/color-picker.tsx create mode 100644 client/src/lib/snapshot-utils.ts diff --git a/client/src/components/dashboard/share-dashboard-dialog.tsx b/client/src/components/dashboard/share-dashboard-dialog.tsx new file mode 100644 index 0000000..e9cb6d9 --- /dev/null +++ b/client/src/components/dashboard/share-dashboard-dialog.tsx @@ -0,0 +1,421 @@ +import { useState } from "react"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogDescription, + DialogFooter, +} from "@/components/ui/dialog"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Textarea } from "@/components/ui/textarea"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { Switch } from "@/components/ui/switch"; +import { Slider } from "@/components/ui/slider"; +import { ColorPicker } from "@/components/ui/color-picker"; +import { CopyIcon, Share2, CheckIcon, Download } from "lucide-react"; +import { useToast } from "@/hooks/use-toast"; +import { DashboardConfig } from "./dashboard-layout"; +import { generateSnapshot } from "@/lib/snapshot-utils"; + +interface ShareDashboardDialogProps { + open: boolean; + onOpenChange: (open: boolean) => void; + dashboard: DashboardConfig; + dataSources: Array<{ + id: string; + name: string; + data: any[]; + fields: Array<{ name: string; type: string }>; + }>; +} + +export function ShareDashboardDialog({ + open, + onOpenChange, + dashboard, + dataSources +}: ShareDashboardDialogProps) { + const { toast } = useToast(); + const [shareUrl, setShareUrl] = useState(""); + const [copied, setCopied] = useState(false); + const [logoUrl, setLogoUrl] = useState(""); + const [companyName, setCompanyName] = useState(""); + const [customTitle, setCustomTitle] = useState(dashboard.name); + const [description, setDescription] = useState(""); + const [primaryColor, setPrimaryColor] = useState("#0f172a"); + const [secondaryColor, setSecondaryColor] = useState("#6366f1"); + const [showDate, setShowDate] = useState(true); + const [showFooter, setShowFooter] = useState(true); + const [footerText, setFooterText] = useState("Generated with Active Directory Management Platform"); + const [expiryDays, setExpiryDays] = useState(7); + const [includeData, setIncludeData] = useState(true); + const [isSharing, setIsSharing] = useState(false); + const [isGenerating, setIsGenerating] = useState(false); + + // When user uploads a logo + const handleLogoUpload = (e: React.ChangeEvent) => { + const file = e.target.files?.[0]; + if (file) { + const reader = new FileReader(); + reader.onloadend = () => { + setLogoUrl(reader.result as string); + }; + reader.readAsDataURL(file); + } + }; + + // Generate a sharable URL with snapshot data + const handleGenerateShareLink = async () => { + setIsSharing(true); + try { + const snapshotData = generateSnapshot({ + dashboard, + dataSources: includeData ? dataSources : [], + branding: { + logoUrl, + companyName, + title: customTitle, + description, + primaryColor, + secondaryColor, + showDate, + showFooter, + footerText, + expiresInDays: expiryDays + } + }); + + // In a real app, this would make an API call to store the snapshot + // For now, we're simulating it with localStorage + const snapshotId = `dash_${Date.now().toString(36)}`; + localStorage.setItem(`snapshot_${snapshotId}`, JSON.stringify(snapshotData)); + + // Generate share URL + const shareUrl = `${window.location.origin}/dashboards/shared/${snapshotId}`; + setShareUrl(shareUrl); + + toast({ + title: "Share link generated", + description: "The share link for this dashboard has been created.", + }); + } catch (error) { + console.error("Error generating share link:", error); + toast({ + title: "Error", + description: "Failed to generate share link. Please try again.", + variant: "destructive", + }); + } finally { + setIsSharing(false); + } + }; + + // Copy the URL to clipboard + const handleCopyLink = () => { + navigator.clipboard.writeText(shareUrl); + setCopied(true); + toast({ + title: "Copied!", + description: "Share link copied to clipboard.", + }); + + setTimeout(() => { + setCopied(false); + }, 2000); + }; + + // Generate a PDF or image snapshot for download + const handleDownloadSnapshot = async () => { + setIsGenerating(true); + try { + const snapshotData = generateSnapshot({ + dashboard, + dataSources: includeData ? dataSources : [], + branding: { + logoUrl, + companyName, + title: customTitle, + description, + primaryColor, + secondaryColor, + showDate, + showFooter, + footerText, + expiresInDays: 0 // Download doesn't expire + } + }); + + // In a real implementation, we would generate a PDF or image here + // For now, we'll just download the JSON data + const dataStr = JSON.stringify(snapshotData, null, 2); + const dataBlob = new Blob([dataStr], { type: 'application/json' }); + const url = URL.createObjectURL(dataBlob); + + const a = document.createElement('a'); + a.href = url; + a.download = `${dashboard.name.replace(/\s+/g, '_')}_snapshot.json`; + document.body.appendChild(a); + a.click(); + + // Clean up + setTimeout(() => { + document.body.removeChild(a); + URL.revokeObjectURL(url); + }, 100); + + toast({ + title: "Snapshot downloaded", + description: "Your dashboard snapshot has been downloaded.", + }); + } catch (error) { + console.error("Error generating snapshot:", error); + toast({ + title: "Error", + description: "Failed to generate snapshot. Please try again.", + variant: "destructive", + }); + } finally { + setIsGenerating(false); + } + }; + + return ( + + + + Share Dashboard: {dashboard.name} + + Create a shareable snapshot of this dashboard with custom branding. + + + + + + Share Link + Branding Options + + + +
+
+ +
+ + +
+
+ +
+ +
+
+ Expires after + + {expiryDays === 0 ? "Never expires" : `${expiryDays} days`} + +
+ setExpiryDays(value[0])} + /> +
+
+ + {!shareUrl ? ( + + ) : ( + <> +
+ + +
+ +
+ + +
+ + )} +
+
+ + +
+
+ + + {logoUrl && ( +
+ Company Logo +
+ )} +
+ +
+ + setCompanyName(e.target.value)} + placeholder="Your Company Name" + /> +
+ +
+ + setCustomTitle(e.target.value)} + placeholder="Dashboard Title" + /> +
+ +
+ +