diff --git a/.gitignore b/.gitignore
index 3b02042..d1caaf8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -40,3 +40,5 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts
+
+certificates
\ No newline at end of file
diff --git a/docker-compose.yml b/docker-compose.yml
index 09d48c0..6662ded 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -64,6 +64,7 @@ services:
environment:
DATABASE_URL: "postgresql://${POSTGRES_USER:-drinktracker}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB:-drinktracker}"
MINIO_ENDPOINT: "minio"
+ WATCHPACK_POLLING: "true"
depends_on:
db:
condition: service_healthy
diff --git a/next.config.mjs b/next.config.mjs
index 842fe14..1c807cb 100644
--- a/next.config.mjs
+++ b/next.config.mjs
@@ -16,6 +16,18 @@ const nextConfig = {
},
],
},
+ async rewrites() {
+ // Proxy image requests to MinIO so URLs work from any device
+ const minioHost = process.env.MINIO_ENDPOINT || "localhost";
+ const minioPort = process.env.MINIO_PORT || "9000";
+ const minioBucket = process.env.MINIO_BUCKET || "drink-images";
+ return [
+ {
+ source: "/minio-images/:path*",
+ destination: `http://${minioHost}:${minioPort}/${minioBucket}/:path*`,
+ },
+ ];
+ },
async headers() {
return [
{
diff --git a/package-lock.json b/package-lock.json
index b62ceed..822c276 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -17,6 +17,7 @@
"bcryptjs": "^3.0.3",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
+ "html5-qrcode": "^2.3.8",
"lucide-react": "^0.575.0",
"next": "14.2.35",
"next-auth": "^5.0.0-beta.25",
@@ -5359,6 +5360,12 @@
"node": ">= 0.4"
}
},
+ "node_modules/html5-qrcode": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/html5-qrcode/-/html5-qrcode-2.3.8.tgz",
+ "integrity": "sha512-jsr4vafJhwoLVEDW3n1KvPnCCXWaQfRng0/EEYk1vNcQGcG/htAdhJX0be8YyqMoSz7+hZvOZSTAepsabiuhiQ==",
+ "license": "Apache-2.0"
+ },
"node_modules/ignore": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
diff --git a/package.json b/package.json
index 188dddc..be4a808 100644
--- a/package.json
+++ b/package.json
@@ -3,7 +3,7 @@
"version": "0.1.0",
"private": true,
"scripts": {
- "dev": "next dev",
+ "dev": "next dev --experimental-https",
"build": "next build",
"start": "next start",
"lint": "next lint"
@@ -18,6 +18,7 @@
"bcryptjs": "^3.0.3",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
+ "html5-qrcode": "^2.3.8",
"lucide-react": "^0.575.0",
"next": "14.2.35",
"next-auth": "^5.0.0-beta.25",
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index de3c8b3..51f11e5 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -279,6 +279,8 @@ model BarItem {
category BarItemCategory
quantity BarItemQuantity @default(FULL)
notes String? @db.Text
+ barcode String?
+ imageUrl String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@ -286,6 +288,7 @@ model BarItem {
@@index([userId])
@@index([userId, category])
+ @@index([userId, barcode])
}
// ─── Recipes ────────────────────────────────────────────────────
diff --git a/src/app/(app)/bar/page.tsx b/src/app/(app)/bar/page.tsx
index 41fa168..de9b452 100644
--- a/src/app/(app)/bar/page.tsx
+++ b/src/app/(app)/bar/page.tsx
@@ -20,7 +20,9 @@ import {
useDeleteBarItem,
} from "@/hooks/use-bar"
import type { BarItem } from "@/hooks/use-bar"
-import { Plus, Wine } from "lucide-react"
+import { BarcodeScanDialog } from "@/components/bar/barcode-scan-dialog"
+import type { BarcodeLookupResult } from "@/hooks/use-barcode-lookup"
+import { Plus, Wine, ScanLine } from "lucide-react"
import type { BarItemCreate } from "@/lib/validators"
export default function BarPage() {
@@ -65,17 +67,40 @@ const CATEGORY_ORDER = [
function BarContent() {
const [addDialogOpen, setAddDialogOpen] = useState(false)
+ const [scanDialogOpen, setScanDialogOpen] = useState(false)
const [editingItem, setEditingItem] = useState