SPB Git

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%
5.5 KB · 199 lines tsx
Raw Blame History
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