Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 55 additions & 3 deletions src/features/comparison/components/result-dashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
"use client";

import { useMemo } from "react";
import { useMemo, useState, useRef } from "react";
import Link from "next/link";
import type { Route } from "next";
import { Check, Copy, ExternalLink, Trophy } from "lucide-react";
import { Check, Copy, ExternalLink, Trophy, Share2, XCircle } from "lucide-react";
import { useSearchParams } from "next/navigation";
import { useClipboardCopy } from "@/hooks";
import { Avatar } from "@/components/layout/avatar";
Expand Down Expand Up @@ -63,6 +63,34 @@ export function ResultDashboard({
const { t } = useTranslation();
const searchParams = useSearchParams();
const { copied, copy } = useClipboardCopy();
const { copy: copyShare } = useClipboardCopy();
const [shareState, setShareState] = useState<"idle" | "success" | "error">("idle");
const shareTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);

const setShareFeedback = (state: "success" | "error") => {
setShareState(state);
if (shareTimerRef.current) clearTimeout(shareTimerRef.current);
shareTimerRef.current = setTimeout(() => setShareState("idle"), 2000);
};

const handleShare = async () => {
const url = window.location.href;
try {
if (navigator.share) {
await navigator.share({ url });
setShareFeedback("success");
} else {
const success = await copyShare(url);
setShareFeedback(success ? "success" : "error");
}
} catch (err: unknown) {
if (err instanceof Error && err.name === "AbortError") {
return;
}
setShareFeedback("error");
}
};

const methodologyHref = useMemo(() => {
const query = searchParams.toString();
return query ? `/scoring-methodology?${query}` : "/scoring-methodology";
Expand Down Expand Up @@ -454,7 +482,31 @@ export function ResultDashboard({
</Card>
) : null}

<div className="flex justify-end">
<div className="flex justify-end gap-2">
<Button
variant="secondary"
size="sm"
onClick={handleShare}
className="flex items-center gap-2"
aria-label={t("results.shareAria")}
>
{shareState === "success" ? (
<>
<Check className="h-4 w-4 text-green-500" />
<span className="text-green-500">{t("results.shareSuccess")}</span>
</>
) : shareState === "error" ? (
<>
<XCircle className="h-4 w-4 text-destructive" />
<span className="text-destructive">{t("results.shareError")}</span>
</>
) : (
<>
<Share2 className="h-4 w-4" />
{t("results.share")}
</>
)}
</Button>
<Button
variant="secondary"
size="sm"
Expand Down
4 changes: 4 additions & 0 deletions src/locales/ar.json
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,10 @@
"results.copy": "نسخ النتيجة",
"results.copyAria": "نسخ نتائج المقارنة إلى الحافظة",
"results.pointsLead": "+{points} نقطة",
"results.share": "مشاركة",
"results.shareAria": "مشاركة رابط المقارنة",
"results.shareSuccess": "تمت مشاركة الرابط!",
"results.shareError": "فشلت مشاركة الرابط",
"score.rawLabel": "الدرجة الخام: {value}",
"results.scoreVersion": "إصدار التقييم",
"section.language": "النتيجة اللغوية",
Expand Down
4 changes: 4 additions & 0 deletions src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,10 @@
"results.copy": "Copy Result",
"results.copyAria": "Copy comparison results to clipboard",
"results.pointsLead": "+{points} points",
"results.share": "Share",
"results.shareAria": "Share comparison link",
"results.shareSuccess": "Link shared!",
"results.shareError": "Failed to share link",
"score.rawLabel": "Raw: {value}",
"results.scoreVersion": "Score version",
"section.language": "Language-focused Score",
Expand Down
Loading