profileShare

rasmusjy / tasteprint

Read-only snapshot

No repository description.

main default branch 169 files Expires Sep 13, 2026, 9:06 AM
ComparePage.tsx 3,668 bytes
1 import { useQuery } from "@tanstack/react-query";
2 import { ArrowLeft, ArrowRight, MapPin, UtensilsCrossed } from "lucide-react";
3 import { Link, useParams } from "react-router-dom";
4 import { Avatar, ErrorState, LoadingScreen, PageHeader } from "../components/Ui";
5 import { useAuth } from "../lib/auth";
6 import { api } from "../lib/api";
7 import type { Destination, TasteComparison } from "../types";
8
9 function CountryList({ title, countries, empty }: { title: string; countries: Destination[]; empty: string }) {
10 return (
11 <section className="compare-column">
12 <h3>{title}</h3>
13 {countries.length ? countries.map(country => (
14 <Link key={country.code} to={`/app/destinations/${country.code}`}>
15 <span style={{ backgroundColor: country.accentColor }}>{country.code}</span>
16 <strong>{country.name}</strong>
17 <ArrowRight size={15} />
18 </Link>
19 )) : <p>{empty}</p>}
20 </section>
21 );
22 }
23
24 export function ComparePage() {
25 const { shareSlug } = useParams();
26 const { user } = useAuth();
27 const comparison = useQuery({
28 queryKey: ["comparison", shareSlug],
29 queryFn: () => api<TasteComparison>(`/api/v1/social/compare/${shareSlug}`),
30 enabled: Boolean(shareSlug),
31 retry: false
32 });
33
34 if (comparison.isLoading) return <LoadingScreen label="Laying two food maps together" />;
35 if (comparison.isError || !comparison.data) return <ErrorState message="This Tasteprint could not be compared. It may be private." action={<Link className="button button-outline" to="/app/profile">Try another</Link>} />;
36 const data = comparison.data;
37
38 return (
39 <div className="page compare-page">
40 <Link className="back-link" to="/app/profile"><ArrowLeft size={16} /> Profile</Link>
41 <PageHeader eyebrow="Taste map match" title="What belongs on your shared table?"
42 copy="This compares dishes, not ratings. Different tastes can still make a very good meal." />
43
44 <section className="match-card">
45 <div className="match-person"><Avatar name={user?.displayName ?? "You"} url={user?.avatarUrl} size="large" /><strong>{user?.displayName}</strong><span>Your map</span></div>
46 <div className="match-score"><span>Taste overlap</span><strong>{data.overlapScore}%</strong><small>{data.sharedDishes} dishes in common</small></div>
47 <div className="match-person"><Avatar name={data.otherUser.displayName} url={data.otherUser.avatarUrl} size="large" /><strong>{data.otherUser.displayName}</strong><span>Their map</span></div>
48 </section>
49
50 {data.suggestedSharedBite && (
51 <section className="shared-bite" style={{ borderColor: data.suggestedSharedBite.destinationAccent }}>
52 <div className="shared-bite-icon"><UtensilsCrossed size={24} /></div>
53 <div><span className="eyebrow">Your next shared bite</span><h2>{data.suggestedSharedBite.name}</h2><p>{data.suggestedSharedBite.description}</p><small><MapPin size={14} /> {data.suggestedSharedBite.destinationName}, {data.suggestedSharedBite.categoryLabel}</small></div>
54 <Link className="button button-primary" to={`/app/destinations/${data.suggestedSharedBite.destinationCode}`}>See the food culture</Link>
55 </section>
56 )}
57
58 <div className="compare-grid">
59 <CountryList title="You both tasted" countries={data.sharedCountries} empty="No shared countries yet." />
60 <CountryList title="Only on your map" countries={data.yourUniqueCountries} empty="Nothing unique yet." />
61 <CountryList title={`Only on ${data.otherUser.displayName.split(" ")[0]}'s map`} countries={data.theirUniqueCountries} empty="Nothing unique yet." />
62 </div>
63 </div>
64 );
65 }
66