spb/vquant Public MIT
VibeQuant — AI-powered institutional-grade financial intelligence platform.
TypeScript 84.3%
Python 11.7%
JavaScript 1.6%
CSS 1.5%
HTML 0.7%
1/*2 * =============================================================================3 * VibeQuant (vquant) — AI-Powered Financial Intelligence Platform4 * -----------------------------------------------------------------------------5 * File: client/src/components/chat/share-button.tsx6 *7 * Author: Simon-Pierre Boucher8 * Contact: contact@spboucher.ai9 * Website: https://www.spboucher.ai10 * Demo: https://www.vquant.ai11 * License: MIT (see LICENSE)12 *13 * Copyright © 2026 Simon-Pierre Boucher. All rights reserved.14 * =============================================================================15 */1617import { Button } from "@/components/ui/button";18import { Share2, Check, Copy } from "lucide-react";19import { useState } from "react";20import { useToast } from "@/hooks/use-toast";21import {22 Dialog,23 DialogContent,24 DialogDescription,25 DialogHeader,26 DialogTitle,27} from "@/components/ui/dialog";28import { Input } from "@/components/ui/input";2930interface ShareButtonProps {31 question: string;32 answer: string;33 toolResults: any[];34 sources: any[];35 customPythonFigures?: any[];36}3738export function ShareButton({ question, answer, toolResults, sources, customPythonFigures }: ShareButtonProps) {39 const [isSharing, setIsSharing] = useState(false);40 const [shareUrl, setShareUrl] = useState<string | null>(null);41 const [isCopied, setIsCopied] = useState(false);42 const { toast } = useToast();4344 const handleShare = async () => {45 if (!question || !answer) {46 toast({47 title: "Error",48 description: "Cannot share an empty report",49 variant: "destructive",50 });51 return;52 }5354 setIsSharing(true);55 56 try {57 const response = await fetch("/api/share", {58 method: "POST",59 headers: {60 "Content-Type": "application/json",61 },62 body: JSON.stringify({63 question,64 answer,65 toolResults,66 sources,67 customPythonFigures,68 }),69 });7071 const data = await response.json();72 73 if (!response.ok) {74 throw new Error(data.error || "Failed to share");75 }7677 // Créer le lien de partage78 const url = `${window.location.origin}/share/${data.shareId}`;79 setShareUrl(url);80 } catch (error) {81 console.error("Share error:", error);82 toast({83 title: "Error",84 description: error instanceof Error ? error.message : "Failed to create share link",85 variant: "destructive",86 });87 } finally {88 setIsSharing(false);89 }90 };9192 const handleCopyLink = async () => {93 if (!shareUrl) return;94 95 try {96 await navigator.clipboard.writeText(shareUrl);97 setIsCopied(true);98 setTimeout(() => setIsCopied(false), 2000);99 100 toast({101 title: "Link copied!",102 description: "The link has been copied to your clipboard",103 });104 } catch (error) {105 // Fallback for mobile106 try {107 const textArea = document.createElement('textarea');108 textArea.value = shareUrl;109 textArea.style.position = 'fixed';110 textArea.style.left = '-999999px';111 document.body.appendChild(textArea);112 textArea.focus();113 textArea.select();114 document.execCommand('copy');115 textArea.remove();116 117 setIsCopied(true);118 setTimeout(() => setIsCopied(false), 2000);119 120 toast({121 title: "Link copied!",122 description: "The link has been copied to your clipboard",123 });124 } catch (fallbackError) {125 toast({126 title: "Error",127 description: "Cannot copy automatically. Please copy the link manually.",128 variant: "destructive",129 });130 }131 }132 };133134 return (135 <>136 <Button137 variant="outline"138 size="sm"139 onClick={handleShare}140 disabled={isSharing || !question || !answer}141 data-testid="button-share"142 className="hover-elevate active-elevate-2"143 >144 {isSharing ? (145 <>146 <Share2 className="w-4 h-4 mr-2 animate-pulse" />147 Creating...148 </>149 ) : (150 <>151 <Share2 className="w-4 h-4 mr-2" />152 Share153 </>154 )}155 </Button>156157 <Dialog open={!!shareUrl} onOpenChange={(open) => !open && setShareUrl(null)}>158 <DialogContent className="sm:max-w-md">159 <DialogHeader>160 <DialogTitle>Share link created!</DialogTitle>161 <DialogDescription>162 Copy this link to share your analysis163 </DialogDescription>164 </DialogHeader>165 <div className="flex items-center space-x-2">166 <div className="grid flex-1 gap-2">167 <Input168 readOnly169 value={shareUrl || ''}170 data-testid="input-share-url"171 className="font-mono text-sm"172 />173 </div>174 <Button175 type="button"176 size="sm"177 onClick={handleCopyLink}178 data-testid="button-copy-link"179 className="hover-elevate active-elevate-2"180 >181 {isCopied ? (182 <>183 <Check className="h-4 w-4 mr-1" />184 Copied185 </>186 ) : (187 <>188 <Copy className="h-4 w-4 mr-1" />189 Copy190 </>191 )}192 </Button>193 </div>194 </DialogContent>195 </Dialog>196 </>197 );198}199