WallpaperMachine
The wallpapers you love. A new home on your Mac.
Find the best Disney cruise deals with our Deal Score system. Filter by ship, duration, and guests.
LifestyleGitHub @robobuckstar-opsOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
Repository information has not been collected yet.
No updates detected yet. GitHub releases will appear here.
Disney cruise planning and deal discovery platform — GatGridCruises.com
Translation pending
Taken from the repository README
Loading the full README.
Read the full README ↗Magically Valuable Disney Cruises
A Disney cruise planning and deal discovery platform. Not a booking site — an ad-supported media and tools property for families who want transparent, honest information before committing to a Disney cruise.
# 1. Clone and install
git clone <repo-url>
cd gatgridcruises
npm install
# 2. Set up environment (optional — app works without it using local seed data)
cp .env.local.example .env.local
# 3. Run dev server
npm run dev
Open http://localhost:3000.
The app runs fully with local seed data out of the box — no Supabase or API keys needed for development.
src/
├── app/ # Next.js App Router (40 pages)
│ ├── page.tsx # Homepage
│ ├── deals/ # Deal grid with filters + sorting
│ ├── sailing/[id]/ # Sailing detail (price chart, itinerary, CTA)
│ ├── ships/ # Ship index + detail pages
│ ├── guides/ # 10 SEO pillar content pages
│ ├── tools/
│ │ ├── cost-calculator/ # True cost calculator (15+ categories)
│ │ ├── flights/ # Flight finder for departure ports
│ │ ├── staterooms/ # Stateroom finder + reviews
│ │ ├── transfers/ # Transfer decision tool
│ │ └── compare/ # Side-by-side sailing comparison
│ ├── hotels/ # Pre-cruise hotel guides by port
│ ├── subscribe/ # Email newsletter signup
│ ├── account/watchlist/ # Saved sailings (localStorage → Supabase)
│ ├── auth/ # Login/signup (magic link)
│ ├── admin/ # Admin panel (dashboard, CRUD, ads)
│ ├── api/ # API routes (subscribe, cron, reviews, alerts)
│ ├── go/[...slug]/ # Affiliate link wrapper with click tracking
│ ├── about/ # About page
│ ├── disclosures/ # FTC affiliate disclosures
│ ├── privacy/ # Privacy policy
│ └── terms/ # Terms of use
├── components/
│ ├── layout/ # Header, Footer
│ └── ui/ # Ad slots, chatbot, charts, cards, etc.
├── data/ # Local seed data (ships, ports, sailings, etc.)
├── lib/ # Utils, data access, auth, scraper, email templates
└── types/ # TypeScript interfaces
supabase/
└── migrations/ # Production database schema
docs/
└── boardwalk-compliance-email.html # Boardwalk Travel Agency email draft
| Route | Description |
|---|---|
/ | Homepage with featured deals, stats, newsletter CTA |
/deals | All sailings with filters, search, sorting, score badges |
/sailing/[id] | Price breakdown, history chart, itinerary, hotels, transfers |
/ships | Fleet index (8 ships) |
/ships/[slug] | Ship detail with staterooms, sailings, amenities |
/hotels | Port hotel index |
/hotels/[port] | Port-specific hotel recommendations |
/tools/cost-calculator | True trip cost calculator (15+ line items) |
/tools/flights | Flight search for departure ports |
/tools/staterooms | Stateroom finder with noise ratings |
/tools/staterooms/reviews | User stateroom reviews |
/tools/transfers | Transfer guide index |
/tools/transfers/[port] | Port-specific transfer recommendations |
/tools/compare | Side-by-side sailing comparison |
/guides | Guide hub (10 articles) |
/guides/disney-cruise-cost-guide | True cost breakdown |
/guides/best-disney-cruise-staterooms | Category-by-category guide |
/guides/port-canaveral-vs-miami | Port comparison |
/guides/first-time-disney-cruise-tips | 25 essential first-timer tips |
/guides/disney-cruise-with-toddlers | Parents guide (ages 0-4) |
/guides/castaway-cay-guide | Private island guide |
/guides/disney-cruise-packing-list | Complete packing list |
/guides/disney-cruise-vs-royal-caribbean | Line comparison |
/guides/best-time-to-book-disney-cruise |
/go/[provider]/[target] wrapper with click loggingThe sailing catalog is a committed snapshot (src/data/sailings.json), so it
goes stale on its own. Two mechanisms keep that from showing on the site:
src/lib/data.ts drops any sailing whose departure
date is before today in America/Chicago, plus any row with no usable lead
price. Nothing is deleted — the read helpers just stop returning those rows,
evaluated per request (the listing pages set dynamic = 'force-dynamic').GET /api/cron/data-freshness runs Monday
and Thursday at 12:00 UTC (07:00 Central) via the crons block in
vercel.json. It sends nothing and writes nothing — it counts expired,
unpriced, and near-term sailings and returns needs_refresh with reasons.Check it by hand at any time:
curl "https://gatgridcruises.com/api/cron/data-freshness?secret=$CRON_SECRET"
It flags a refresh when the catalog is empty, has fewer than 60 bookable
sailings, has fewer than 10 departing inside 90 days, is more than 14 days past
its newest updated_at, is more than 25% expired, or contains unpriced rows.
It always returns 200 — a stale catalog is a job for a human, not a cron retry.
When it flags, regenerate and commit the catalog:
APIFY_TOKEN=… APIFY_DATASET_ID=… npx tsx scripts/fetch-apify-data.ts
GET /api/cron/abandoned-quotes reports quote requests that are still sitting
in the new-lead stage roughly 48 hours after they arrived — someone asked for a
price and nobody replied. It runs daily at 13:30 UTC via vercel.json.
It identifies and prepares. It does not send. There is no send path in the
route or in src/lib/abandoned-quotes.ts. Each flagged lead comes back with a
drafted subject and plain-text body for a human to read, edit, and send from
the real inbox. The response reports emails_sent: 0 and
sending_enabled: false so this is obvious from the payload alone.
curl "https://gatgridcruises.com/api/cron/abandoned-quotes?secret=$CRON_SECRET"
A lead is flagged when its Source came from a quote form (free-quote,
request-this-sailing, group-cruise, concierge, transfer), its pipeline
stage is still unworked, and first contact was 48 hours to 30 days ago. Past 30
days the nurture drip owns the lead and it drops off this report.
| Env var | Default | Effect |
|---|---|---|
CRON_SECRET | — | Required, same as every other cron route |
ABANDONED_QUOTES_MARK_CRM | off | Set to true to also stamp each flagged lead's Next Follow-Up with today's date. Still sends no email. |
The MVP includes comprehensive local seed data:
# Install Vercel CLI
npm i -g vercel
# Deploy
vercel
# Set environment variables
vercel env add NEXT_PUBLIC_SUPABASE_URL
vercel env add NEXT_PUBLIC_SUPABASE_ANON_KEY
vercel env add SUPABASE_SERVICE_ROLE_KEY
vercel env add RESEND_API_KEY
vercel env add CRON_SECRET
vercel env add ADMIN_ALLOWED_EMAILS
Or connect your GitHub repo to Vercel for automatic deployments on every push.
supabase/migrations/001_initial_schema.sql.env.localsrc/lib/data.ts to use Supabase queries instead of local dataBuilt with care by Grayson Starbuck.
| Booking timing + price data |
/guides/disney-cruise-food-guide | Every restaurant ranked |
/subscribe | Newsletter signup |
/account/watchlist | Saved sailings |
/auth/login | Magic link login |
/auth/signup | Account creation |
/admin | Admin dashboard |
/admin/sailings | Manage sailings |
/admin/ships | Manage ships |
/admin/ports | Manage ports |
/admin/hotels | Manage hotels |
/admin/staterooms | Manage staterooms |
/admin/transfers | Manage transfers |
/admin/subscribers | Manage subscribers |
/admin/ads | Ad slot management + revenue dashboard |
/about | About page |
/disclosures | FTC disclosures |
/privacy | Privacy policy |
/terms | Terms of use |