Remove dead code: unused components, duplicate logic, orphaned route
- Delete Hero, ReviewsSection, TrustedBrands components (never imported) - Delete /api/admin/orders/[orderId]/complete route (never called; order state transitions go through /status instead) - Extract maxColorsFor() from ProductCard and CartDrawer into src/lib/colors.ts to eliminate the duplicated implementation Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
f0b60f123d
commit
9dd4aff35e
@ -1,43 +0,0 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { Client, Environment } from 'square'
|
||||
|
||||
function getClient() {
|
||||
return new Client({
|
||||
accessToken: process.env.SQUARE_ACCESS_TOKEN!,
|
||||
environment: process.env.SQUARE_ENVIRONMENT === 'production'
|
||||
? Environment.Production
|
||||
: Environment.Sandbox,
|
||||
})
|
||||
}
|
||||
|
||||
export async function POST(
|
||||
_req: NextRequest,
|
||||
{ params }: { params: { orderId: string } }
|
||||
) {
|
||||
try {
|
||||
const client = getClient()
|
||||
const { orderId } = params
|
||||
|
||||
// Retrieve current order to get version (required for optimistic concurrency)
|
||||
const { result: { order } } = await client.ordersApi.retrieveOrder(orderId)
|
||||
if (!order) return NextResponse.json({ error: 'Order not found' }, { status: 404 })
|
||||
|
||||
await client.ordersApi.updateOrder(orderId, {
|
||||
order: {
|
||||
locationId: order.locationId!,
|
||||
state: 'COMPLETED',
|
||||
version: order.version,
|
||||
fulfillments: (order.fulfillments ?? []).map((f) => ({
|
||||
uid: f.uid,
|
||||
state: 'COMPLETED',
|
||||
})),
|
||||
},
|
||||
idempotencyKey: `complete-${orderId}`,
|
||||
})
|
||||
|
||||
return NextResponse.json({ ok: true })
|
||||
} catch (err) {
|
||||
console.error('[admin/orders/complete]', err)
|
||||
return NextResponse.json({ error: 'Failed to complete order' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
@ -15,6 +15,7 @@ import ColorPicker from './ColorPicker'
|
||||
import BookingRequestPanel from './BookingRequestPanel'
|
||||
import CalendarPicker from './CalendarPicker'
|
||||
import type { CartEntry } from '@/context/CartContext'
|
||||
import { maxColorsFor } from '@/lib/colors'
|
||||
|
||||
/** Syncs a string state value to localStorage. Hydrates after mount. */
|
||||
function useStoredString(key: string, initial: string): [string, (v: string) => void] {
|
||||
@ -32,15 +33,6 @@ function useStoredString(key: string, initial: string): [string, (v: string) =>
|
||||
return [value, set]
|
||||
}
|
||||
|
||||
function maxColorsFor(name: string): number | null {
|
||||
const n = name.toLowerCase()
|
||||
if (/arch|column/.test(n)) return 4
|
||||
if (/\b11["''″]|\b11[- ]?inch/.test(n)) return 1
|
||||
if (/number.{0,10}sculpt|sculpt.{0,10}number/.test(n)) return 4
|
||||
if (/ultimate/.test(n)) return 4
|
||||
return null
|
||||
}
|
||||
|
||||
type Step = 'cart' | 'delivery' | 'info' | 'payment'
|
||||
|
||||
const STEP_TITLES: Record<Step, string> = {
|
||||
|
||||
@ -1,87 +0,0 @@
|
||||
/**
|
||||
* Hero section — matches the layout of beachpartyballoons.com's index.html:
|
||||
* full-width image with the logo overlaid, then centered address + CTA.
|
||||
*/
|
||||
export default function Hero() {
|
||||
return (
|
||||
<>
|
||||
{/* Scrolling announcement bar */}
|
||||
<div className="update">
|
||||
<div id="message">
|
||||
🎈 Walk-ins welcome! · Delivery available across CT · Over 70 latex colors in stock · Custom arrangements made while you wait · 203.283.5626
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Hero image with logo overlay */}
|
||||
<div className="container is-justify-content-center padding">
|
||||
<figure className="image" style={{ position: 'relative' }}>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img src="/images/classic/hero.webp" alt="Beach Party Balloons" />
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
className="is-overlay"
|
||||
src="/images/logo/BeachPartyBalloons-logo.webp"
|
||||
alt="Beach Party Balloons logo"
|
||||
style={{ width: '50%', height: 'auto', margin: 'auto' }}
|
||||
/>
|
||||
</figure>
|
||||
</div>
|
||||
|
||||
{/* Address + contact */}
|
||||
<div className="is-flex-direction-column is-dark">
|
||||
<h1 className="is-size-3" style={{ textAlign: 'center' }}>
|
||||
Shop Online
|
||||
</h1>
|
||||
<h2 className="is-size-4" style={{ textAlign: 'center' }}>
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href="https://maps.app.goo.gl/gRk6NztgMRUsSVJf9"
|
||||
>
|
||||
554 Boston Post Road, Milford, CT 06460
|
||||
</a>
|
||||
</h2>
|
||||
<h2 className="is-size-4" style={{ textAlign: 'center' }}>
|
||||
<a href="tel:203.283.5626">203.283.5626</a>
|
||||
</h2>
|
||||
<div className="py-2 has-text-centered">
|
||||
<a href="https://beachpartyballoons.com/contact/">
|
||||
<button className="button is-info">Contact Us</button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Intro image + copy */}
|
||||
<div className="container is-justify-content-center padding">
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img src="/images/organic/img1.webp" alt="Organic balloon arrangement" />
|
||||
</div>
|
||||
|
||||
<p className="is-size-5 has-text-centered">Walk-ins welcome!</p>
|
||||
<p className="is-size-6 has-text-centered">
|
||||
Pick up a balloon arrangement for birthdays or any occasion. We will make
|
||||
it while you wait!
|
||||
</p>
|
||||
<p className="is-size-6 has-text-centered">
|
||||
We have hundreds of foil balloon choices in stock and over 70 latex colors!
|
||||
</p>
|
||||
|
||||
<p className="is-size-5 has-text-centered">
|
||||
...Or consult with one of our designers about your balloon decorating needs.
|
||||
</p>
|
||||
|
||||
<div className="container is-justify-content-center padding">
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img src="/images/organic/img2.webp" alt="Delivery balloon arrangement" />
|
||||
</div>
|
||||
|
||||
<p className="is-size-5 has-text-centered">Delivery available!</p>
|
||||
<p className="is-size-5 has-text-centered">
|
||||
Have your balloons delivered! Our delivery staff will make sure your order
|
||||
arrives safe and sound!
|
||||
</p>
|
||||
|
||||
<hr className="section-divider" />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -4,20 +4,12 @@ import { useState } from 'react'
|
||||
import type { CatalogItem } from '@/data/mock-catalog'
|
||||
import ColorPicker from './ColorPicker'
|
||||
import { fmt } from '@/lib/format'
|
||||
import { maxColorsFor } from '@/lib/colors'
|
||||
|
||||
interface Props {
|
||||
item: CatalogItem
|
||||
}
|
||||
|
||||
function maxColorsFor(name: string): number | null {
|
||||
const n = name.toLowerCase()
|
||||
if (/arch|column/.test(n)) return 4
|
||||
if (/\b11["''″]|\b11[- ]?inch/.test(n)) return 1
|
||||
if (/number.{0,10}sculpt|sculpt.{0,10}number/.test(n)) return 4
|
||||
if (/ultimate/.test(n)) return 4
|
||||
return null
|
||||
}
|
||||
|
||||
/** Lowest stock count across tracked variations. null = nothing tracked. */
|
||||
function lowestTrackedInventory(item: CatalogItem): number | null {
|
||||
const tracked = item.variations.filter((v) => v.inventory !== null)
|
||||
|
||||
@ -1,68 +0,0 @@
|
||||
const REVIEWS = [
|
||||
{
|
||||
text: '"Stunning balloon arch and super easy to work with. Our guests loved it!"',
|
||||
author: '— Jordan M., Corporate Event',
|
||||
},
|
||||
{
|
||||
text: '"Professional setup and gorgeous colors. Made our celebration unforgettable."',
|
||||
author: '— Maria L., Birthday Party',
|
||||
},
|
||||
{
|
||||
text: '"Fast, friendly, and beautiful designs. Will book again for our next event."',
|
||||
author: '— Alex T., School Event',
|
||||
},
|
||||
]
|
||||
|
||||
export default function ReviewsSection() {
|
||||
return (
|
||||
<>
|
||||
<hr className="section-divider" />
|
||||
<section className="section reviews-section">
|
||||
<div className="container has-text-centered">
|
||||
<h2 className="is-size-3">Google Reviews</h2>
|
||||
<p className="is-size-6 has-text-grey">
|
||||
See what clients are saying, or leave a review after your event.
|
||||
</p>
|
||||
|
||||
<div className="reviews-summary mt-4">
|
||||
<div className="reviews-stars" aria-label="Average rating: 4.9 out of 5">
|
||||
<span>★</span><span>★</span><span>★</span><span>★</span><span>★</span>
|
||||
</div>
|
||||
<p className="is-size-5 has-text-weight-semibold">
|
||||
Rated 4.9 on Google
|
||||
</p>
|
||||
<p className="is-size-7 has-text-grey">Based on 100+ reviews</p>
|
||||
</div>
|
||||
|
||||
<div className="reviews-grid mt-5">
|
||||
{REVIEWS.map((r, i) => (
|
||||
<article key={i} className="review-card">
|
||||
<p>{r.text}</p>
|
||||
<p className="review-author">{r.author}</p>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="buttons is-centered mt-4">
|
||||
<a
|
||||
className="button is-info"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href="https://www.google.com/maps/place/Beach+Party+Balloons/@41.2305385,-73.0657635,17z"
|
||||
>
|
||||
Read reviews
|
||||
</a>
|
||||
<a
|
||||
className="button is-light"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href="https://g.page/r/CUEOXUF1nSDQEBE/review"
|
||||
>
|
||||
Leave a review
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -1,40 +0,0 @@
|
||||
const LOGOS = [
|
||||
{ src: '/images/trusted/512px-Subway_icon.svg.webp', alt: 'Subway', dark: false },
|
||||
{ src: '/images/trusted/Yale_press_logo.webp', alt: 'Yale', dark: false },
|
||||
{ src: '/images/trusted/256px-Quinnipiac_University_logo_(2017).svg.webp', alt: 'Quinnipiac University', dark: false },
|
||||
{ src: '/images/trusted/512px-University_of_New_Haven_logo.webp', alt: 'University of New Haven', dark: false },
|
||||
{ src: '/images/trusted/Planet_Fitness_(2).webp', alt: 'Planet Fitness', dark: false },
|
||||
{ src: '/images/trusted/Mohegan-Sun-Logo.webp', alt: 'Mohegan Sun', dark: false },
|
||||
{ src: '/images/trusted/Post_university_of_conn_logo.webp', alt: 'Post University', dark: false },
|
||||
{ src: '/images/trusted/logo-full-color.webp', alt: 'Edge Fitness', dark: false },
|
||||
{ src: '/images/trusted/lincoln-culinary.webp', alt: 'Lincoln Culinary Institute', dark: false },
|
||||
{ src: '/images/trusted/amazon.webp', alt: 'Amazon', dark: false },
|
||||
{ src: '/images/trusted/woodwinds-2024-logo-white.webp', alt: 'The Woodwinds', dark: true },
|
||||
{ src: '/images/trusted/sallys-apizza.webp', alt: "Sally's Apizza", dark: false },
|
||||
]
|
||||
|
||||
export default function TrustedBrands() {
|
||||
return (
|
||||
<section className="section trusted-section">
|
||||
<div className="container">
|
||||
<div className="has-text-centered mb-5">
|
||||
<h2 className="is-size-3">Trusted by</h2>
|
||||
<p className="is-size-6 has-text-grey">
|
||||
Brands and organizations we have had the joy of celebrating with.
|
||||
</p>
|
||||
</div>
|
||||
<div className="trusted-logos">
|
||||
{LOGOS.map((logo) => (
|
||||
<figure
|
||||
key={logo.alt}
|
||||
className={`trusted-logo ${logo.dark ? 'trusted-logo--dark' : ''}`}
|
||||
>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img src={logo.src} alt={`${logo.alt} logo`} loading="lazy" />
|
||||
</figure>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
8
estore/src/lib/colors.ts
Normal file
8
estore/src/lib/colors.ts
Normal file
@ -0,0 +1,8 @@
|
||||
export function maxColorsFor(name: string): number | null {
|
||||
const n = name.toLowerCase()
|
||||
if (/arch|column/.test(n)) return 4
|
||||
if (/\b11["''″]|\b11[- ]?inch/.test(n)) return 1
|
||||
if (/number.{0,10}sculpt|sculpt.{0,10}number/.test(n)) return 4
|
||||
if (/ultimate/.test(n)) return 4
|
||||
return null
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user