initial commmit

This commit is contained in:
dusk.gecko 2025-02-17 20:18:31 -06:00
parent 88c0a9715f
commit eba76a1ec4
36 changed files with 5249 additions and 0 deletions

28
.dockerignore Normal file
View File

@ -0,0 +1,28 @@
# Ignore node_modules folder (dependencies will be installed in the container)
node_modules
# Ignore build output
.next
# Ignore macOS-specific files
__MACOSX
# Ignore IDE/editor config files (e.g., VSCode, etc.)
.vscode
.idea
# Ignore log files
*.log
# Ignore environment-specific configuration
.env
.env.local
.env.*.local
# Ignore README and documentation files (optional)
README.md
# Ignore version control files
.git
.gitignore

10
.eslintrc.json Normal file
View File

@ -0,0 +1,10 @@
{
"extends": ["next/core-web-vitals", "plugin:tailwindcss/recommended"],
"settings": {
/* Support tailwind rules inside class utility functions */
"tailwindcss": {
"callees": ["clsx", "cva", "cn"],
"config": "tailwind.config.ts"
}
}
}

36
.gitignore vendored Normal file
View File

@ -0,0 +1,36 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts

7
.prettierrc.json Normal file
View File

@ -0,0 +1,7 @@
{
"tabWidth": 2,
"semi": true,
"singleQuote": false,
"trailingComma": "all",
"printWidth": 100
}

27
Dockerfile Normal file
View File

@ -0,0 +1,27 @@
# Use the official Node.js image as a base image
FROM node:18-alpine
# Set the working directory inside the container
WORKDIR /app
# Copy the package.json and pnpm-lock.yaml first to install dependencies (helps with caching)
COPY package.json pnpm-lock.yaml ./
# Install pnpm globally
RUN npm install -g pnpm
# Install project dependencies
RUN pnpm install --frozen-lockfile
# Copy the rest of the application code
COPY . .
# Build the Next.js app
RUN pnpm build
# Expose the port the app will run on
EXPOSE 3000
# Set the command to run the app in production mode
CMD ["pnpm", "start"]

BIN
__MACOSX/._package.json Normal file

Binary file not shown.

BIN
__MACOSX/._pnpm-lock.yaml Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

17
components.json Normal file
View File

@ -0,0 +1,17 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "src/app/globals.css",
"baseColor": "slate",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils"
}
}

4
next.config.mjs Normal file
View File

@ -0,0 +1,4 @@
/** @type {import('next').NextConfig} */
const nextConfig = {};
export default nextConfig;

44
package.json Normal file
View File

@ -0,0 +1,44 @@
{
"name": "reweb-starter",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@radix-ui/react-context-menu": "^2.2.6",
"@radix-ui/react-scroll-area": "^1.2.3",
"@radix-ui/react-separator": "^1.1.2",
"@radix-ui/react-slot": "^1.1.2",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"date-fns": "^4.1.0",
"lucide-react": "^0.381.0",
"next": "14.2.3",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"sharp": "^0.33.5",
"tailwind-merge": "^2.6.0",
"tailwindcss-animate": "^1.0.7"
},
"devDependencies": {
"@types/node": "^20.17.19",
"@types/react": "^18.3.18",
"@types/react-dom": "^18.3.5",
"eslint": "^8.57.1",
"eslint-config-next": "14.2.3",
"eslint-plugin-tailwindcss": "^3.18.0",
"postcss": "^8.5.2",
"prettier": "^3.5.1",
"tailwindcss": "^3.4.17",
"typescript": "^5.7.3"
},
"pnpm": {
"onlyBuiltDependencies": [
"sharp"
]
}
}

4436
pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load Diff

8
postcss.config.mjs Normal file
View File

@ -0,0 +1,8 @@
/** @type {import('postcss-load-config').Config} */
const config = {
plugins: {
tailwindcss: {},
},
};
export default config;

BIN
public/images/Thumbs.db Normal file

Binary file not shown.

BIN
public/images/coffee1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 MiB

BIN
public/images/coffee2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

BIN
public/images/interior.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

BIN
public/images/lucys.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

BIN
public/images/sandwich.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 MiB

BIN
src/app/Thumbs.db Normal file

Binary file not shown.

BIN
src/app/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

55
src/app/globals.css Normal file
View File

@ -0,0 +1,55 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 0 0% 16%;
--card: 0 0% 100%;
--card-foreground: 0 0% 4%;
--popover: 0 0% 96%;
--popover-foreground: 0 0% 4%;
--primary: 250 93% 66%;
--primary-foreground: 210 40% 98%;
--secondary: 240 100% 97%;
--secondary-foreground: 0 0% 9%;
--muted: 210 40% 96%;
--muted-foreground: 200 6% 50%;
--accent: 0 0% 88%;
--accent-foreground: 0 0% 9%;
--destructive: 0 84% 60%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 90%;
--input: 0 0% 90%;
--ring: 0 0% 4%;
--sidebar-background: 0 0% 98%;
--sidebar-foreground: 240 5.3% 26.1%;
--sidebar-primary: 240 5.9% 10%;
--sidebar-primary-foreground: 0 0% 98%;
--sidebar-accent: 240 4.8% 95.9%;
--sidebar-accent-foreground: 240 5.9% 10%;
--sidebar-border: 220 13% 91%;
--sidebar-ring: 217.2 91.2% 59.8%;
--radius: 0.5rem;
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}

41
src/app/layout.tsx Normal file
View File

@ -0,0 +1,41 @@
import "./globals.css";
import type { Metadata } from "next";
import { Inter, Rubik } from "next/font/google";
import { cn } from "@/lib/utils";
const fontSans = Inter({
variable: "--font-sans",
subsets: ["latin"],
});
const fontHeading = Rubik({
variable: "--font-heading",
subsets: ["latin"],
});
export const metadata: Metadata = {
title: "Lucy's",
description: "Welcome to Lucy's! Located in Little Falls, MN",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body
className={cn(
"min-h-screen font-sans antialiased",
fontSans.variable,
fontHeading.variable
)}
>
{children}
</body>
</html>
);
}

9
src/app/menu/page.tsx Normal file
View File

@ -0,0 +1,9 @@
import { Section2 } from "@/components/section-2";
export default function MenuPage() {
return (
<>
<Section2 />
</>
);
}

9
src/app/page.tsx Normal file
View File

@ -0,0 +1,9 @@
import { Section } from "@/components/section";
export default function HomePage() {
return (
<>
<Section />
</>
);
}

View File

@ -0,0 +1,102 @@
import Image from "next/image";
import { Card, CardHeader, CardTitle, CardDescription, CardContent } from "@/components/ui/card";
import { Separator } from "@/components/ui/separator";
export function Section2() {
return (
<div className="max-w-4xl mx-auto p-6 space-y-8">
<div className="space-y-4">
<h2 className="text-3xl font-heading">Coffee Menu</h2>
<div className="grid gap-6 md:grid-cols-2">
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<CardTitle>Espresso</CardTitle>
<span className="font-bold">$3.50</span>
</div>
<CardDescription className="text-muted-foreground">
Pure and intense shot of coffee
</CardDescription>
</CardHeader>
<CardContent>
<Image
alt="Espresso"
src="/images/placeholder.png"
width={300}
height={200}
className="rounded-lg object-cover w-full h-48"
/>
</CardContent>
</Card>
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<CardTitle>Cappuccino</CardTitle>
<span className="font-bold">$4.50</span>
</div>
<CardDescription className="text-muted-foreground">
Espresso with steamed milk foam
</CardDescription>
</CardHeader>
<CardContent>
<Image
alt="Cappuccino"
src="/images/placeholder.png"
width={300}
height={200}
className="rounded-lg object-cover w-full h-48"
/>
</CardContent>
</Card>
</div>
</div>
<Separator className="my-8" />
<div className="space-y-4">
<h2 className="text-3xl font-heading">Food Menu</h2>
<div className="grid gap-6 md:grid-cols-2">
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<CardTitle>Avocado Toast</CardTitle>
<span className="font-bold">$8.99</span>
</div>
<CardDescription className="text-muted-foreground">
Sourdough bread with mashed avocado and seeds
</CardDescription>
</CardHeader>
<CardContent>
<Image
alt="Avocado Toast"
src="/images/placeholder.png"
width={300}
height={200}
className="rounded-lg object-cover w-full h-48"
/>
</CardContent>
</Card>
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<CardTitle>Croissant</CardTitle>
<span className="font-bold">$3.99</span>
</div>
<CardDescription className="text-muted-foreground">
Freshly baked butter croissant
</CardDescription>
</CardHeader>
<CardContent>
<Image
alt="Croissant"
src="/images/placeholder.png"
width={300}
height={200}
className="rounded-lg object-cover w-full h-48"
/>
</CardContent>
</Card>
</div>
</div>
</div>
);
}

169
src/components/section.tsx Normal file
View File

@ -0,0 +1,169 @@
"use client";
import Image from "next/image";
import Link from "next/link";
import { MapPin, Mail, Phone } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Card, CardHeader, CardTitle, CardDescription, CardContent } from "@/components/ui/card";
export function Section() {
const openMapsApp = () => {
// URL to the location on Google Maps
const googleMapsUrl = "https://maps.app.goo.gl/FQSY4ww5WZ9mATRP9";
// Default coordinates for opening on maps
const coordinates = "500 Broadway East, Little Falls, MN 56345";
// Check the platform (Android or iOS)
const userAgent = navigator.userAgent || navigator.vendor;
// For Android (Google Maps)
if (/android/i.test(userAgent)) {
// Open Google Maps app using the deep link
window.location.href = `google.navigation:q=${coordinates}`;
}
// For iOS (Apple Maps)
else if (/iPad|iPhone|iPod/.test(userAgent)) {
// Open Apple Maps using the deep link
window.location.href = `maps:0,0?q=${coordinates}`;
} else {
// For other platforms, open Google Maps in the browser
window.open(googleMapsUrl, "_blank");
}
};
return (
<div className="min-h-screen">
<section className="relative h-[600px]">
<Image
alt="Cafe interior"
src="/images/coffee1.jpg"
fill
className="object-cover brightness-50"
/>
<div className="absolute inset-0 flex flex-col items-center justify-center text-center">
<div className="flex items-center justify-center p-8">
<Image
alt="Company Logo"
src="/images/lucys.png"
width={480}
height={128}
className="h-128 w-auto object-contain h-128"
/>
</div>
<h1 className="font-heading text-4xl md:text-6xl font-bold text-white mb-4">
Welcome to Lucy&apos;s
</h1>
<p className="text-lg md:text-xl text-white mb-8 max-w-2xl mx-auto">Brewed Fresh, Just Around the Corner</p>
<Button
size="lg"
asChild
variant="outline"
className="flex items-center gap-2 rounded-lg"
>
<Link href="/menu">
<span>View Menu</span>
</Link>
</Button>
</div>
</section>
<section className="py-24 px-6">
<div className="max-w-6xl mx-auto grid grid-cols-1 md:grid-cols-3 gap-12">
<Card className="text-center">
<CardHeader>
<CardTitle className="font-heading">Fresh Coffee</CardTitle>
<CardDescription className="text-muted-foreground">
Locally roasted beans, expertly prepared
</CardDescription>
</CardHeader>
<CardContent>
<Image
alt="Coffee"
src="/images/coffee2.jpg"
width={200}
height={200}
className="mx-auto rounded-full"
/>
</CardContent>
</Card>
<Card className="text-center">
<CardHeader>
<CardTitle className="font-heading">Hand Crafted Sandwiches</CardTitle>
<CardDescription className="text-muted-foreground">
Made fresh daily with premium ingredients
</CardDescription>
</CardHeader>
<CardContent>
<Image
alt="Sandwiches"
src="/images/sandwich.jpg"
width={200}
height={200}
className="mx-auto rounded-full"
/>
</CardContent>
</Card>
<Card className="text-center">
<CardHeader>
<CardTitle className="font-heading">Cozy Atmosphere</CardTitle>
<CardDescription className="text-muted-foreground">
Perfect for work or relaxation
</CardDescription>
</CardHeader>
<CardContent>
<Image
alt="Cafe interior"
src="/images/interior.jpg"
width={200}
height={200}
className="mx-auto rounded-full"
/>
</CardContent>
</Card>
</div>
</section>
<section className="bg-muted py-16 px-6">
<div className="max-w-4xl mx-auto text-center">
<h2 className="font-heading text-3xl font-bold mb-8">Visit Us Today</h2>
<p className="text-muted-foreground mb-6">500 Broadway East, Little Falls, MN 56345</p>
<p className="text-muted-foreground mb-6">Open Monday - Saturday: 7am -2pm</p>
<div className="flex justify-center">
<Button
variant="outline"
size="sm"
className="flex gap-1"
onClick={openMapsApp} // Add onClick to open the maps app
>
<MapPin className="h-3 w-3" />
<span className="text-muted-foreground text-xs">Get Directions</span>
</Button>
</div>
</div>
</section>
<section className="py-16 px-6">
<div className="max-w-4xl mx-auto text-center">
<h2 className="font-heading text-3xl font-bold mb-8">Contact Us</h2>
<div className="flex flex-col items-center gap-4">
<Link href="mailto:lucys56344@gmail.com">
<div className="flex items-center gap-2">
<Mail className="h-5 w-5 text-muted-foreground" />
<p className="text-muted-foreground">lucys56345@gmail.com</p>
</div>
</Link>
<Link href="tel:3204140400">
<div className="flex items-center gap-2">
<Phone className="h-5 w-5 text-muted-foreground" />
<p className="text-muted-foreground">(320) 414-0400</p>
</div>
</Link>
</div>
</div>
</section>
<footer className="bg-background border-t py-8 px-6">
<div className="max-w-6xl mx-auto text-center">
<p className="text-muted-foreground">© 2025 Lucys. All rights reserved.</p>
</div>
</footer>
</div>
);
}

View File

@ -0,0 +1,49 @@
import * as React from "react";
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
const buttonVariants = cva(
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8",
icon: "size-10",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
},
);
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean;
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button";
return (
<Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />
);
},
);
Button.displayName = "Button";
export { Button, buttonVariants };

View File

@ -0,0 +1,56 @@
import * as React from "react";
import { cn } from "@/lib/utils";
const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("rounded-lg border bg-card text-card-foreground shadow-sm", className)}
{...props}
/>
),
);
Card.displayName = "Card";
const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => (
<div ref={ref} className={cn("flex flex-col space-y-1.5 p-6", className)} {...props} />
),
);
CardHeader.displayName = "CardHeader";
const CardTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>(
({ className, ...props }, ref) => (
<h3
ref={ref}
className={cn("text-2xl font-semibold leading-none tracking-tight", className)}
{...props}
/>
),
);
CardTitle.displayName = "CardTitle";
const CardDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
<p ref={ref} className={cn("text-sm text-muted-foreground", className)} {...props} />
));
CardDescription.displayName = "CardDescription";
const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => (
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
),
);
CardContent.displayName = "CardContent";
const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => (
<div ref={ref} className={cn("flex items-center p-6 pt-0", className)} {...props} />
),
);
CardFooter.displayName = "CardFooter";
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent };

View File

@ -0,0 +1,26 @@
"use client";
import * as React from "react";
import * as SeparatorPrimitive from "@radix-ui/react-separator";
import { cn } from "@/lib/utils";
const Separator = React.forwardRef<
React.ElementRef<typeof SeparatorPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
>(({ className, orientation = "horizontal", decorative = true, ...props }, ref) => (
<SeparatorPrimitive.Root
ref={ref}
decorative={decorative}
orientation={orientation}
className={cn(
"shrink-0 bg-border",
orientation === "horizontal" ? "h-px w-full" : "h-full w-px",
className,
)}
{...props}
/>
));
Separator.displayName = SeparatorPrimitive.Root.displayName;
export { Separator };

6
src/lib/utils.ts Normal file
View File

@ -0,0 +1,6 @@
import { type ClassValue, clsx } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}

84
tailwind.config.ts Normal file
View File

@ -0,0 +1,84 @@
import type { Config } from "tailwindcss";
import { fontFamily } from "tailwindcss/defaultTheme";
const config = {
darkMode: ["class"],
content: [
"./pages/**/*.{ts,tsx}",
"./components/**/*.{ts,tsx}",
"./app/**/*.{ts,tsx}",
"./src/**/*.{ts,tsx}",
],
prefix: "",
theme: {
container: {
center: true,
padding: "2rem",
screens: {
"2xl": "1200px",
},
},
extend: {
colors: {
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
secondary: {
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))",
},
destructive: {
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))",
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))",
},
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
},
popover: {
DEFAULT: "hsl(var(--popover))",
foreground: "hsl(var(--popover-foreground))",
},
card: {
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))",
},
sidebar: {
DEFAULT: "hsl(var(--sidebar-background))",
foreground: "hsl(var(--sidebar-foreground))",
primary: "hsl(var(--sidebar-primary))",
"primary-foreground": "hsl(var(--sidebar-primary-foreground))",
accent: "hsl(var(--sidebar-accent))",
"accent-foreground": "hsl(var(--sidebar-accent-foreground))",
border: "hsl(var(--sidebar-border))",
ring: "hsl(var(--sidebar-ring))",
},
},
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
},
keyframes: {},
animation: {},
fontFamily: {
sans: ["var(--font-sans)", ...fontFamily.sans],
heading: ["var(--font-heading)", ...fontFamily.sans],
},
},
},
plugins: [require("tailwindcss-animate")],
} satisfies Config;
export default config;

26
tsconfig.json Normal file
View File

@ -0,0 +1,26 @@
{
"compilerOptions": {
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}