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 BookingRequestPanel from './BookingRequestPanel'
|
||||||
import CalendarPicker from './CalendarPicker'
|
import CalendarPicker from './CalendarPicker'
|
||||||
import type { CartEntry } from '@/context/CartContext'
|
import type { CartEntry } from '@/context/CartContext'
|
||||||
|
import { maxColorsFor } from '@/lib/colors'
|
||||||
|
|
||||||
/** Syncs a string state value to localStorage. Hydrates after mount. */
|
/** Syncs a string state value to localStorage. Hydrates after mount. */
|
||||||
function useStoredString(key: string, initial: string): [string, (v: string) => void] {
|
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]
|
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'
|
type Step = 'cart' | 'delivery' | 'info' | 'payment'
|
||||||
|
|
||||||
const STEP_TITLES: Record<Step, string> = {
|
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 type { CatalogItem } from '@/data/mock-catalog'
|
||||||
import ColorPicker from './ColorPicker'
|
import ColorPicker from './ColorPicker'
|
||||||
import { fmt } from '@/lib/format'
|
import { fmt } from '@/lib/format'
|
||||||
|
import { maxColorsFor } from '@/lib/colors'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
item: CatalogItem
|
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. */
|
/** Lowest stock count across tracked variations. null = nothing tracked. */
|
||||||
function lowestTrackedInventory(item: CatalogItem): number | null {
|
function lowestTrackedInventory(item: CatalogItem): number | null {
|
||||||
const tracked = item.variations.filter((v) => v.inventory !== 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