perf: modified routing button in cafe detail page

This commit is contained in:
Afra ‌ 2026-07-18 13:56:22 +03:30
parent 5b7f904dbe
commit 165c7fd0eb
2 changed files with 58 additions and 15 deletions

View file

@ -168,18 +168,51 @@ function ImageCarousel({ images }: { images: string[] }) {
); );
} }
async function openNavigationPicker(name: string, lat: number, lng: number) { function NavigationSheet({
const mapsUrl = `https://www.google.com/maps/dir/?api=1&destination=${lat},${lng}`; isOpen,
onClose,
lat,
lng,
}: {
isOpen: boolean;
onClose: () => void;
lat: number;
lng: number;
}) {
if (!isOpen) return null;
if (navigator.share) { const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
try {
await navigator.share({ title: name, url: mapsUrl }); const apps: { label: string; url: string }[] = [
} catch { { label: "نشان", url: `https://neshan.org/maps/@${lat},${lng},15z` },
// user dismissed the native share sheet { label: "بلد", url: `https://balad.ir/p?lat=${lat}&lng=${lng}` },
} { label: "Waze", url: `https://waze.com/ul?ll=${lat},${lng}&navigate=yes` },
} else { { label: "Google Maps", url: `https://www.google.com/maps/dir/?api=1&destination=${lat},${lng}` },
window.open(mapsUrl, "_blank"); ...(isIOS ? [{ label: "Apple Maps", url: `maps://?daddr=${lat},${lng}` }] : []),
} ];
return createPortal(
<>
<div className="fixed inset-0 z-9998 bg-black/50" onClick={onClose} />
<div className="fixed inset-x-0 bottom-0 z-9999 rounded-t-3xl bg-white px-5 pb-8 pt-4">
<div className="mx-auto mb-4 h-1 w-10 rounded-full bg-gray-200" />
<h3 className="mb-4 text-right font-semibold text-gray-900">مسیریابی با</h3>
<div className="flex flex-col gap-3">
{apps.map((app) => (
<button
key={app.label}
type="button"
onClick={() => { window.open(app.url, "_blank"); onClose(); }}
className="w-full rounded-2xl border border-gray-200 py-3 text-center text-gray-900 active:bg-gray-50"
>
{app.label}
</button>
))}
</div>
</div>
</>,
document.body,
);
} }
export default function CafeDetail() { export default function CafeDetail() {
@ -187,6 +220,7 @@ export default function CafeDetail() {
const navigate = useNavigate(); const navigate = useNavigate();
const now = useNow(); const now = useNow();
const [isReservationOpen, setIsReservationOpen] = useState(false); const [isReservationOpen, setIsReservationOpen] = useState(false);
const [isNavigationOpen, setIsNavigationOpen] = useState(false);
const [schedules, setSchedules] = useState<CafeSchedule[]>([]); const [schedules, setSchedules] = useState<CafeSchedule[]>([]);
const [selectedEventUuid] = useState(""); const [selectedEventUuid] = useState("");
const [schedule, setSchedule] = useState<CafeEventSchedule | undefined>( const [schedule, setSchedule] = useState<CafeEventSchedule | undefined>(
@ -332,9 +366,7 @@ export default function CafeDetail() {
<button <button
type="button" type="button"
onClick={() => onClick={() => setIsNavigationOpen(true)}
openNavigationPicker(cafe.name, mapPosition[0], mapPosition[1])
}
className="mt-4 w-full rounded-2xl border border-[#223D5D] py-3 text-center text-gray-900" className="mt-4 w-full rounded-2xl border border-[#223D5D] py-3 text-center text-gray-900"
> >
مسیریابی مسیریابی
@ -368,6 +400,12 @@ export default function CafeDetail() {
ticketPrice={schedule ? Number(schedule.ticket_price) : 0} ticketPrice={schedule ? Number(schedule.ticket_price) : 0}
cafeMatchScheduleUuid={schedule?.schedule_uuid ?? ""} cafeMatchScheduleUuid={schedule?.schedule_uuid ?? ""}
/> />
<NavigationSheet
isOpen={isNavigationOpen}
onClose={() => setIsNavigationOpen(false)}
lat={mapPosition[0]}
lng={mapPosition[1]}
/>
</div> </div>
); );
} }

View file

@ -244,6 +244,7 @@ function MyReservationsRow() {
function CafeCard({ cafe }: { cafe: Cafe }) { function CafeCard({ cafe }: { cafe: Cafe }) {
const navigate = useNavigate() const navigate = useNavigate()
const remainingTickets = cafe.schedules?.[0]?.remaining_tickets const remainingTickets = cafe.schedules?.[0]?.remaining_tickets
const firstImage = cafe.media.find((m) => m.type === 'image')
return ( return (
<button <button
@ -251,7 +252,11 @@ function CafeCard({ cafe }: { cafe: Cafe }) {
onClick={() => navigate(`/cafe/${cafe.uuid}`, { state: { cafe } })} onClick={() => navigate(`/cafe/${cafe.uuid}`, { state: { cafe } })}
className="flex w-full items-center justify-start gap-4 rounded-2xl bg-white p-4 text-right shadow-sm" className="flex w-full items-center justify-start gap-4 rounded-2xl bg-white p-4 text-right shadow-sm"
> >
<div className="h-32 w-36 shrink-0 rounded-xl bg-gray-200" /> <div className="h-32 w-36 shrink-0 overflow-hidden rounded-xl bg-gray-200">
{firstImage && (
<img src={firstImage.url} alt="" className="h-full w-full object-cover" />
)}
</div>
<div className="flex flex-col items-start gap-2"> <div className="flex flex-col items-start gap-2">
<h3 className="font-semibold text-gray-900">{cafe.name}</h3> <h3 className="font-semibold text-gray-900">{cafe.name}</h3>
{remainingTickets !== undefined && ( {remainingTickets !== undefined && (