- AppRouter wraps every route (except /amber-callback and the 404) in ONE
<Route element={<Layout/>}> with an <Outlet/>; the chrome now persists across
navigation instead of being torn down and rebuilt on every page.
- Layout: children optional (falls back to <Outlet/>); the map is full-bleed via
a route check (isFullBleed) instead of a per-page noPadding prop.
- All 59 pages + CraftProfileForm/ErrorState/NewsletterIssueView now render
fragments instead of <Layout> (structure-preserving), imports dropped.
Removes the whole remount class: chrome no longer re-runs its hooks per
navigation, Sasquatch chat state survives navigation, and the header can't
re-open Amber on every page change.
79 lines
2.7 KiB
TypeScript
79 lines
2.7 KiB
TypeScript
import { useParams } from 'react-router-dom';
|
|
import { useSeoMeta } from '@unhead/react';
|
|
import { Card, CardContent } from '@/components/ui/card';
|
|
import { Skeleton } from '@/components/ui/skeleton';
|
|
import { LoginArea } from '@/components/auth/LoginArea';
|
|
import { useCurrentUser } from '@/hooks/useCurrentUser';
|
|
import { useTour } from '@/hooks/useTours';
|
|
import { Route, AlertTriangle } from 'lucide-react';
|
|
import CreateTourPage from './CreateTourPage';
|
|
import NotFound from './NotFound';
|
|
|
|
/**
|
|
* Edit an existing tour. Loads the tour by pubkey + identifier and hands it to
|
|
* CreateTourPage as `existing`, which prefills every field, reuses the same
|
|
* addressable identifier, and preserves unmanaged tags on save. Only the tour's
|
|
* owner may edit.
|
|
*/
|
|
export default function EditTourPage() {
|
|
const { pubkey, identifier } = useParams<{ pubkey: string; identifier: string }>();
|
|
const { user } = useCurrentUser();
|
|
const { data: tour, isLoading, error } = useTour(identifier || '', pubkey);
|
|
|
|
useSeoMeta({
|
|
title: tour?.title ? `Edit ${tour.title} - SkateSpots` : 'Edit Tour - SkateSpots',
|
|
description: 'Edit your skate tour — update stops, dates and details.',
|
|
});
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<>
|
|
<div className="container px-4 sm:px-6 py-6 sm:py-8 max-w-3xl space-y-4">
|
|
<Skeleton className="h-6 w-32" />
|
|
<Skeleton className="h-10 w-64" />
|
|
<Skeleton className="h-40 w-full" />
|
|
<Skeleton className="h-64 w-full" />
|
|
</div>
|
|
</>
|
|
);
|
|
}
|
|
|
|
if (error || !tour) {
|
|
return <NotFound />;
|
|
}
|
|
|
|
if (!user) {
|
|
return (
|
|
<>
|
|
<div className="container px-4 py-8">
|
|
<Card className="max-w-xl mx-auto border-dashed">
|
|
<CardContent className="py-12 text-center">
|
|
<Route className="h-12 w-12 mx-auto mb-4 text-muted-foreground/50" />
|
|
<h2 className="text-xl font-bold mb-2">Login Required</h2>
|
|
<p className="text-muted-foreground mb-6">You need to log in to edit this tour.</p>
|
|
<LoginArea className="justify-center" />
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
</>
|
|
);
|
|
}
|
|
|
|
if (user.pubkey !== tour.pubkey) {
|
|
return (
|
|
<>
|
|
<div className="container px-4 py-8">
|
|
<Card className="max-w-xl mx-auto border-destructive">
|
|
<CardContent className="py-12 text-center">
|
|
<AlertTriangle className="h-12 w-12 mx-auto mb-4 text-destructive" />
|
|
<h2 className="text-xl font-bold mb-2">Access Denied</h2>
|
|
<p className="text-muted-foreground">You can only edit tours you created.</p>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
</>
|
|
);
|
|
}
|
|
|
|
return <CreateTourPage existing={tour} />;
|
|
}
|