feat: initial commit — storefront, convex backend, and shared packages

Completes the first milestone of The Pet Loft ecommerce platform:
- apps/storefront: full customer-facing Next.js app with HeroUI (cart,
  checkout, orders, wishlist, product detail, shop, search, auth)
- convex/: serverless backend with schema, queries, mutations, actions,
  HTTP routes, Stripe/Shippo integrations, and co-located tests
- packages/types, packages/utils, packages/convex: shared workspace packages

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-04 09:31:18 +03:00
commit cc15338ad9
361 changed files with 45005 additions and 0 deletions

View File

@@ -0,0 +1,79 @@
"use client";
import Image from "next/image";
import { Button, Input } from "@heroui/react";
const NEWSLETTER_IMAGE = "/content/newsletter-dog.png";
function EnvelopeIcon({ className }: { className?: string }) {
return (
<svg className={className} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z" />
<polyline points="22,6 12,13 2,6" />
</svg>
);
}
export function NewsletterSection() {
return (
<section
aria-label="Newsletter signup"
className="relative max-w-7xl mx-auto w-full px-4 md:px-8 mb-12"
>
<div className="relative overflow-hidden bg-[#eaf7d9] rounded-[32px] md:rounded-[48px] p-6 sm:p-10 md:p-12 lg:p-16 w-full min-h-[460px] md:min-h-[400px] flex flex-col md:flex-row items-center md:items-stretch">
{/* Left Content */}
<div className="relative z-20 w-full md:w-[55%] lg:w-1/2 flex flex-col justify-center items-center md:items-start space-y-6 md:space-y-8 max-w-[400px] lg:max-w-md pt-2 md:pt-0 mb-[210px] sm:mb-[300px] md:mb-0 text-center md:text-left mx-auto md:mx-0">
<h2 className="font-[family-name:var(--font-fraunces)] font-medium text-2xl md:text-3xl text-neutral-900 tracking-tight flex flex-col md:block w-full">
<span className="text-balance mx-auto md:mx-0 max-w-[280px] md:max-w-none">
Inner peace, inner peas...
</span>
<span className="font-bold mt-2 md:mt-0 md:ml-1 text-balance">
Newsletter please
</span>
<span className="mt-1 md:mt-0 md:ml-1">🐶...</span>
</h2>
<form
className="w-full"
onSubmit={(e) => e.preventDefault()}
noValidate
>
{/* Mobile: stacked. Desktop (md+): combined pill */}
<div className="flex flex-col gap-2 md:flex-row md:gap-0 md:items-center md:bg-white md:rounded-full md:p-1.5 md:pl-4 md:shadow-sm md:overflow-hidden md:h-[64px]">
<div className="flex items-center bg-white rounded-full h-12 px-4 shadow-sm md:bg-transparent md:shadow-none md:rounded-none md:px-0 md:h-full flex-1 min-w-0">
<EnvelopeIcon className="size-[18px] shrink-0 text-neutral-400 mr-2" />
<Input
id="newsletter-email"
name="newsletter-email"
type="email"
placeholder="Enter your email"
aria-label="Email for newsletter"
className="flex-1 bg-transparent border-transparent shadow-none rounded-none px-0 h-full text-sm md:text-base min-w-0 focus-visible:border-transparent focus-visible:ring-0"
/>
</div>
<Button
type="submit"
className="bg-[#51a67f] hover:bg-[#43906d] text-white rounded-full px-8 h-12 md:h-full w-full md:w-auto font-medium text-sm md:text-base shadow-none transition-colors border-none shrink-0"
>
Subscribe
</Button>
</div>
</form>
</div>
{/* Right Content - Dog Image */}
<div className="absolute bottom-0 left-0 right-0 md:left-auto md:right-0 w-full md:w-[55%] h-[210px] sm:h-[340px] md:h-[110%] z-10 pointer-events-none flex justify-center md:justify-end">
<Image
src={NEWSLETTER_IMAGE}
alt="Peaceful dog"
fill
className="object-contain object-bottom md:object-right-bottom"
sizes="(max-width: 768px) 100vw, 50vw"
priority={false}
/>
</div>
</div>
</section>
);
}