SkateSpots/src/pages/EditTourPage.tsx
shakespeare.diy 9d069cb2f0 Add tour editing — the gap flagged in the addressable-events audit (1.2.33)
Tours (kind 30823) were the only major addressable entity with no edit path: an
organizer had to delete and recreate (losing the URL/identifier) just to fix a
typo or price. Rather than duplicate the enormous multi-category tour form (and
risk schema drift / the exact tag-clobber bugs fixed earlier this session), I
made the existing CreateTourPage do double duty:

- CreateTourPage now accepts an optional `existing?: SkateTour`. When present it
  prefills every field (all four categories: local/national/international/
  destruction — including challenges, prize distribution, film-release, partner
  shop, cities/countries, coords, dates), reuses the same `d` identifier, and
  adapts copy (title, submit button, toast).
- On save it PRESERVES unmanaged tags (sponsors, tags from other clients) and
  keeps any existing paid `featured` window — same "preserve unmanaged tags"
  discipline as the event/shop edit fixes, since 30823 is addressable and
  re-publishing replaces the whole tag set.
- Guarded the category→duration reset effect so it no longer clobbers the
  prefilled duration on the first render in edit mode.
- The paid FeaturedUpgrade upsell is hidden when editing (existing status is
  carried through automatically).

New EditTourPage wrapper loads the tour, gates to the owner (login / access-
denied states), and renders <CreateTourPage existing={tour} />. Added the
/edit-tour/:pubkey/:identifier route and an owner-only Edit button on
TourDetailPage beside the existing Delete control.

This closes the one concrete gap the addressable-events audit surfaced: every
entity now has both a working edit path (preserving unmanaged tags) and owner
management.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-09-07 22:42:34 +07:00

74 lines
2.6 KiB
TypeScript

import { useParams } from 'react-router-dom';
import { Layout } from '@/components/Layout';
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);
if (isLoading) {
return (
<Layout>
<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>
</Layout>
);
}
if (error || !tour) {
return <NotFound />;
}
if (!user) {
return (
<Layout>
<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>
</Layout>
);
}
if (user.pubkey !== tour.pubkey) {
return (
<Layout>
<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>
</Layout>
);
}
return <CreateTourPage existing={tour} />;
}