Skip to content

Commit 9c00738

Browse files
authored
Initial setup, ui, structure (#2)
* chore: remove old files * chore: update packages * feat: initial ui, basic setup, files
1 parent bf77346 commit 9c00738

18 files changed

Lines changed: 3161 additions & 121 deletions

package.json

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,19 @@
2222
"packageManager": "pnpm@10.13.1",
2323
"type": "module",
2424
"dependencies": {
25-
"react": "19.1.0",
26-
"react-dom": "19.1.0",
25+
"dotenv": "^17.2.2",
26+
"lucide-react": "^0.544.0",
2727
"next": "15.5.3",
28-
"dotenv": "^17.2.2"
28+
"react": "19.1.0",
29+
"react-dom": "19.1.0"
2930
},
3031
"devDependencies": {
31-
"@tailwindcss/postcss": "^4",
32-
"tailwindcss": "^4",
3332
"@biomejs/biome": "2.2.0",
3433
"@commitlint/cli": "^19.8.1",
3534
"@commitlint/config-conventional": "^19.8.1",
35+
"@tailwindcss/postcss": "^4",
3636
"husky": "^9.1.7",
37-
"lint-staged": "^16.1.6"
37+
"lint-staged": "^16.1.6",
38+
"tailwindcss": "^4"
3839
}
3940
}

pnpm-lock.yaml

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/app/favicon.ico

-21.1 KB
Binary file not shown.

src/app/globals.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,4 @@ body {
2323
background: var(--background);
2424
color: var(--foreground);
2525
font-family: Arial, Helvetica, sans-serif;
26-
}
26+
}

src/app/layout.js

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,29 @@
1-
import { Geist, Geist_Mono } from "next/font/google";
2-
import "./globals.css";
3-
4-
const geistSans = Geist({
5-
variable: "--font-geist-sans",
6-
subsets: ["latin"],
7-
});
1+
"use client";
82

9-
const geistMono = Geist_Mono({
10-
variable: "--font-geist-mono",
11-
subsets: ["latin"],
12-
});
3+
import { Inter } from "next/font/google";
4+
import "./globals.css";
5+
import { ThemeProvider } from "../contexts/ThemeContext";
6+
import { LanguageProvider } from "../contexts/LanguageContext";
7+
import { ToastProvider } from "../components/ToastProvider";
138

14-
export const metadata = {
15-
title: "Create Next App",
16-
description: "Generated by create next app",
17-
};
9+
const inter = Inter({ subsets: ["latin"] });
1810

1911
export default function RootLayout({ children }) {
2012
return (
21-
<html lang="en">
22-
<body
23-
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
24-
>
25-
{children}
13+
<html lang="en" suppressHydrationWarning>
14+
<head>
15+
<title>PastePick - Smart Toothpaste Analyzer</title>
16+
<meta
17+
name="description"
18+
content="Analyze toothpaste ingredients for safer, smarter choices with PastePick"
19+
/>
20+
</head>
21+
<body className={inter.className} suppressHydrationWarning>
22+
<ThemeProvider>
23+
<LanguageProvider>
24+
<ToastProvider>{children}</ToastProvider>
25+
</LanguageProvider>
26+
</ThemeProvider>
2627
</body>
2728
</html>
2829
);

src/app/page.js

Lines changed: 91 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,103 +1,100 @@
1-
import Image from "next/image";
1+
"use client";
2+
3+
import { useState } from "react";
4+
import BottomNavigation from "../components/BottomNavigation";
5+
import HomePage from "../components/pages/HomePage";
6+
import ScanPage from "../components/pages/ScanPage";
7+
8+
import SettingsPage from "../components/pages/SettingsPage";
9+
import ProductPage from "../components/pages/ProductPage";
10+
import CameraScanner from "../components/CameraScanner";
211

312
export default function Home() {
4-
return (
5-
<div className="font-sans grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20">
6-
<main className="flex flex-col gap-[32px] row-start-2 items-center sm:items-start">
7-
<Image
8-
className="dark:invert"
9-
src="/next.svg"
10-
alt="Next.js logo"
11-
width={180}
12-
height={38}
13-
priority
14-
/>
15-
<ol className="font-mono list-inside list-decimal text-sm/6 text-center sm:text-left">
16-
<li className="mb-2 tracking-[-.01em]">
17-
Get started by editing{" "}
18-
<code className="bg-black/[.05] dark:bg-white/[.06] font-mono font-semibold px-1 py-0.5 rounded">
19-
src/app/page.js
20-
</code>
21-
.
22-
</li>
23-
<li className="tracking-[-.01em]">
24-
Save and see your changes instantly.
25-
</li>
26-
</ol>
13+
const [activeTab, setActiveTab] = useState("home");
14+
const [currentView, setCurrentView] = useState("main"); // 'main', 'product', or 'camera'
15+
const [scannedProduct, setScannedProduct] = useState(null);
2716

28-
<div className="flex gap-4 items-center flex-col sm:flex-row">
29-
<a
30-
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:w-auto"
31-
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
32-
target="_blank"
33-
rel="noopener noreferrer"
34-
>
35-
<Image
36-
className="dark:invert"
37-
src="/vercel.svg"
38-
alt="Vercel logomark"
39-
width={20}
40-
height={20}
41-
/>
42-
Deploy now
43-
</a>
44-
<a
45-
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 w-full sm:w-auto md:w-[158px]"
46-
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
47-
target="_blank"
48-
rel="noopener noreferrer"
49-
>
50-
Read our docs
51-
</a>
52-
</div>
53-
</main>
54-
<footer className="row-start-3 flex gap-[24px] flex-wrap items-center justify-center">
55-
<a
56-
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
57-
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
58-
target="_blank"
59-
rel="noopener noreferrer"
60-
>
61-
<Image
62-
aria-hidden
63-
src="/file.svg"
64-
alt="File icon"
65-
width={16}
66-
height={16}
17+
const handleProductScanned = (product = null) => {
18+
setScannedProduct(product);
19+
setCurrentView("product");
20+
};
21+
22+
const handleStartCamera = () => {
23+
setCurrentView("camera");
24+
};
25+
26+
const handleCameraCapture = (imageData) => {
27+
// Here you would process the captured image
28+
// For now, we'll simulate the scan result
29+
const mockProduct = {
30+
name: "Scanned Toothpaste Product",
31+
brand: "Unknown Brand",
32+
image: "📷",
33+
overallScore: 78,
34+
};
35+
setScannedProduct(mockProduct);
36+
setCurrentView("product");
37+
};
38+
39+
const handleCameraClose = () => {
40+
setCurrentView("main");
41+
};
42+
43+
const handleBackToMain = () => {
44+
setCurrentView("main");
45+
setScannedProduct(null);
46+
};
47+
48+
const renderPage = () => {
49+
switch (activeTab) {
50+
case "home":
51+
return (
52+
<HomePage
53+
onProductScanned={handleProductScanned}
54+
onStartCamera={handleStartCamera}
6755
/>
68-
Learn
69-
</a>
70-
<a
71-
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
72-
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
73-
target="_blank"
74-
rel="noopener noreferrer"
75-
>
76-
<Image
77-
aria-hidden
78-
src="/window.svg"
79-
alt="Window icon"
80-
width={16}
81-
height={16}
56+
);
57+
case "scan":
58+
return (
59+
<ScanPage
60+
onProductScanned={handleProductScanned}
61+
onStartCamera={handleStartCamera}
8262
/>
83-
Examples
84-
</a>
85-
<a
86-
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
87-
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
88-
target="_blank"
89-
rel="noopener noreferrer"
90-
>
91-
<Image
92-
aria-hidden
93-
src="/globe.svg"
94-
alt="Globe icon"
95-
width={16}
96-
height={16}
63+
);
64+
case "recent":
65+
return <RecentPage onProductScanned={handleProductScanned} />;
66+
case "settings":
67+
return <SettingsPage />;
68+
default:
69+
return (
70+
<HomePage
71+
onProductScanned={handleProductScanned}
72+
onStartCamera={handleStartCamera}
9773
/>
98-
Go to nextjs.org →
99-
</a>
100-
</footer>
74+
);
75+
}
76+
};
77+
78+
return (
79+
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-green-50">
80+
{currentView === "product"
81+
? <ProductPage product={scannedProduct} onBack={handleBackToMain} />
82+
: currentView === "camera"
83+
? <CameraScanner
84+
onClose={handleCameraClose}
85+
onCapture={handleCameraCapture}
86+
/>
87+
: <>
88+
{/* Page Content */}
89+
<main className="pt-safe-area">{renderPage()}</main>
90+
91+
{/* Bottom Navigation */}
92+
<BottomNavigation
93+
activeTab={activeTab}
94+
onTabChange={setActiveTab}
95+
onStartCamera={handleStartCamera}
96+
/>
97+
</>}
10198
</div>
10299
);
103100
}

src/components/BottomNavigation.js

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { Home, Heart, Scan, Clock, Settings } from "lucide-react";
2+
import { useLanguage } from "../contexts/LanguageContext";
3+
4+
export default function BottomNavigation({
5+
activeTab,
6+
onTabChange,
7+
onStartCamera,
8+
}) {
9+
const { t } = useLanguage();
10+
11+
const tabs = [
12+
{ id: "home", label: t("home"), icon: Home },
13+
{ id: "scan", label: t("scan"), icon: Scan },
14+
{ id: "settings", label: t("settings"), icon: Settings },
15+
];
16+
17+
return (
18+
<div className="fixed bottom-0 left-0 right-0 bg-white border-t border-gray-200 px-2 py-2 safe-area-pb">
19+
<div className="flex justify-around items-center max-w-md mx-auto">
20+
{tabs.map((tab) => {
21+
const Icon = tab.icon;
22+
const isActive = activeTab === tab.id;
23+
const isScan = tab.id === "scan";
24+
25+
return (
26+
<button
27+
key={tab.id}
28+
onClick={() => {
29+
if (isScan && onStartCamera) {
30+
onStartCamera();
31+
} else {
32+
onTabChange(tab.id);
33+
}
34+
}}
35+
className={`flex flex-col items-center justify-center min-w-0 flex-1 py-2 px-1 transition-all duration-200 ${
36+
isScan ? "transform -translate-y-2" : ""
37+
}`}
38+
>
39+
{/* Special styling for scan button */}
40+
{isScan
41+
? <div
42+
className={`p-3 rounded-full shadow-lg transition-colors ${
43+
isActive
44+
? "bg-blue-600 text-white"
45+
: "bg-blue-600 text-white hover:bg-blue-700"
46+
}`}
47+
>
48+
<Icon size={24} />
49+
</div>
50+
: <div
51+
className={`p-2 rounded-lg transition-colors ${
52+
isActive
53+
? "text-blue-600 bg-blue-50"
54+
: "text-gray-500 hover:text-gray-700"
55+
}`}
56+
>
57+
<Icon size={20} />
58+
</div>}
59+
60+
{/* Label */}
61+
<span
62+
className={`text-xs mt-1 font-medium transition-colors ${
63+
isActive ? "text-blue-600" : "text-gray-500"
64+
} ${isScan ? "text-blue-600" : ""}`}
65+
>
66+
{tab.label}
67+
</span>
68+
</button>
69+
);
70+
})}
71+
</div>
72+
</div>
73+
);
74+
}

0 commit comments

Comments
 (0)