Skip to content

Commit 7869a24

Browse files
committed
the data is working properly
1 parent d60869a commit 7869a24

File tree

10 files changed

+113
-62
lines changed

10 files changed

+113
-62
lines changed

drizzle.config.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,16 @@ import type { Config } from "drizzle-kit";
22
import * as dotenv from "dotenv";
33
import { resolve } from "path";
44

5-
// 🔥 Explicitly load .env.local
5+
// Load your env file explicitly
66
dotenv.config({ path: resolve(__dirname, ".env.local") });
77

88
export default {
9-
schema: "./src/lib/db/schema.ts", // adjust if you use a different path
9+
schema: "./src/lib/db/schema.ts",
1010
out: "./drizzle/migrations",
11-
dialect: "postgresql",
11+
dialect: "postgresql", // 👈 REQUIRED and must be a literal
1212
dbCredentials: {
13-
url: process.env.DATABASE_URL!,
13+
url: process.env.DATABASE_URL!, // 👈 Make sure this exists
1414
},
1515
strict: true,
16+
schemaFilter: ["public"], // optional, just to scope schema
1617
} satisfies Config;

drizzle/migrations/0000_puzzling_sally_floyd.sql renamed to drizzle/migrations/0000_ordinary_doctor_faustus.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ CREATE TABLE "events" (
66
"venue" varchar(255) NOT NULL,
77
"date" timestamp NOT NULL,
88
"time" varchar(50),
9-
"image" varchar(500) NOT NULL,
9+
"image" "bytea",
1010
"created_at" timestamp DEFAULT now() NOT NULL,
1111
"updated_at" timestamp DEFAULT now() NOT NULL,
1212
CONSTRAINT "events_slug_unique" UNIQUE("slug")

drizzle/migrations/meta/0000_snapshot.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"id": "a0ee70f6-983a-423a-b947-b21e3ef07c49",
2+
"id": "4518ee12-206e-490c-ade5-179ba41f07e6",
33
"prevId": "00000000-0000-0000-0000-000000000000",
44
"version": "7",
55
"dialect": "postgresql",
@@ -53,9 +53,9 @@
5353
},
5454
"image": {
5555
"name": "image",
56-
"type": "varchar(500)",
56+
"type": "bytea",
5757
"primaryKey": false,
58-
"notNull": true
58+
"notNull": false
5959
},
6060
"created_at": {
6161
"name": "created_at",

drizzle/migrations/meta/_journal.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
{
66
"idx": 0,
77
"version": "7",
8-
"when": 1756925082563,
9-
"tag": "0000_puzzling_sally_floyd",
8+
"when": 1758033961329,
9+
"tag": "0000_ordinary_doctor_faustus",
1010
"breakpoints": true
1111
}
1212
]

package-lock.json

Lines changed: 23 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"Docker-run": "docker run -p 3000:3000 nextjs_tempalate"
2424
},
2525
"dependencies": {
26+
"@neondatabase/serverless": "^1.0.1",
2627
"date-fns": "^4.1.0",
2728
"dotenv": "^17.2.2",
2829
"drizzle-kit": "^0.31.4",

src/app/api/events/route.ts

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { db } from "@/lib/db";
22
import { events } from "@/lib/db/schema";
3-
import { eq } from "drizzle-orm";
43
import { NextResponse } from "next/server";
54

65
export async function GET() {
@@ -9,8 +8,37 @@ export async function GET() {
98
}
109

1110
export async function POST(req: Request) {
12-
const data = await req.json();
13-
const slug = data.title.toLowerCase().replace(/\s+/g, "-");
14-
const created = await db.insert(events).values({ ...data, slug }).returning();
11+
const formData = await req.formData();
12+
13+
const title = formData.get("title") as string;
14+
const description = formData.get("description") as string;
15+
const venue = formData.get("venue") as string;
16+
const date = formData.get("date") as string;
17+
const time = formData.get("time") as string | null;
18+
19+
// image may or may not exist
20+
const imageFile = formData.get("image") as File | null;
21+
let imageBuffer: Buffer | null = null;
22+
23+
if (imageFile) {
24+
const arrayBuffer = await imageFile.arrayBuffer();
25+
imageBuffer = Buffer.from(arrayBuffer);
26+
}
27+
28+
const slug = title.toLowerCase().replace(/\s+/g, "-");
29+
30+
const created = await db
31+
.insert(events)
32+
.values({
33+
slug,
34+
title,
35+
description,
36+
venue,
37+
date: new Date(date), // timestamp
38+
time: time ?? null,
39+
image: imageBuffer, // store blob
40+
})
41+
.returning();
42+
1543
return NextResponse.json(created[0], { status: 201 });
1644
}

src/app/events/new/page.tsx

Lines changed: 34 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -17,46 +17,40 @@ export default function NewEventPage() {
1717
venue: "",
1818
date: new Date(),
1919
time: "",
20-
image: "", // will store uploaded image URL
2120
});
22-
const [uploading, setUploading] = useState(false);
23-
24-
async function handleImageUpload(file: File) {
25-
const data = new FormData();
26-
data.append("file", file);
27-
28-
setUploading(true);
29-
const res = await fetch("/api/events/upload", {
30-
method: "POST",
31-
body: data,
32-
});
33-
setUploading(false);
34-
35-
if (!res.ok) throw new Error("Upload failed");
36-
const { url } = await res.json();
37-
setForm((prev) => ({ ...prev, image: url }));
38-
}
21+
const [imageFile, setImageFile] = useState<File | null>(null);
22+
const [saving, setSaving] = useState(false);
3923

4024
async function handleSubmit(e: React.FormEvent) {
41-
e.preventDefault();
42-
const formattedDate = form.date.toISOString().split("T")[0];
43-
44-
await fetch("/api/events", {
45-
method: "POST",
46-
headers: { "Content-Type": "application/json" },
47-
body: JSON.stringify({
48-
title: form.title,
49-
description: form.description,
50-
venue: form.venue,
51-
date: formattedDate,
52-
time: form.time,
53-
image: form.image,
54-
}),
55-
});
56-
57-
router.push("/events");
25+
e.preventDefault();
26+
setSaving(true);
27+
28+
const formattedDate = form.date?.toISOString().split("T")[0] ?? "";
29+
30+
const data = new FormData();
31+
32+
// append normal fields safely
33+
data.append("title", form.title ?? "");
34+
data.append("description", form.description ?? "");
35+
data.append("venue", form.venue ?? "");
36+
data.append("date", formattedDate);
37+
data.append("time", form.time ?? "");
38+
39+
// append file only if selected
40+
if (imageFile) {
41+
data.append("image", imageFile);
5842
}
5943

44+
await fetch("/api/events", {
45+
method: "POST",
46+
body: data,
47+
});
48+
49+
setSaving(false);
50+
router.push("/events");
51+
}
52+
53+
6054
return (
6155
<form onSubmit={handleSubmit} className="max-w-lg mx-auto p-6 space-y-4">
6256
<h1 className="text-2xl font-bold">Add New Event</h1>
@@ -108,25 +102,22 @@ export default function NewEventPage() {
108102
/>
109103
</div>
110104

111-
{/* Image Upload */}
105+
{/* Image (optional) */}
112106
<div>
113-
<label className="block mb-1 font-medium">Image</label>
107+
<label className="block mb-1 font-medium">Image (optional)</label>
114108
<input
115109
type="file"
116110
accept="image/*"
117-
onChange={(e) => e.target.files?.[0] && handleImageUpload(e.target.files[0])}
111+
onChange={(e) => setImageFile(e.target.files?.[0] || null)}
118112
/>
119-
{uploading && <p className="text-sm text-gray-500">Uploading...</p>}
120-
{form.image && (
121-
<img src={form.image} alt="Uploaded" className="mt-2 w-32 h-32 object-cover rounded" />
122-
)}
123113
</div>
124114

125115
<button
126116
type="submit"
117+
disabled={saving}
127118
className="bg-black text-white px-4 py-2 rounded hover:bg-gray-800"
128119
>
129-
Save
120+
{saving ? "Saving..." : "Save"}
130121
</button>
131122
</form>
132123
);

src/app/globals.css

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
@import "tailwindcss";
22

3-
:root {
3+
/* :root {
44
--background: #ffffff;
55
--foreground: #171717;
66
}
@@ -23,4 +23,4 @@ body {
2323
background: var(--background);
2424
color: var(--foreground);
2525
font-family: Arial, Helvetica, sans-serif;
26-
}
26+
} */

src/lib/db/schema.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1-
import { pgTable, text, varchar, timestamp, uuid } from "drizzle-orm/pg-core";
1+
// src/lib/db/schema.ts
2+
import { pgTable, text, varchar, timestamp, uuid, customType } from "drizzle-orm/pg-core";
3+
4+
// define bytea type manually
5+
const bytea = customType<{ data: Buffer; driverData: Buffer }>({
6+
dataType() {
7+
return "bytea";
8+
},
9+
});
210

311
export const events = pgTable("events", {
412
id: uuid("id").defaultRandom().primaryKey(),
@@ -8,7 +16,7 @@ export const events = pgTable("events", {
816
venue: varchar("venue", { length: 255 }).notNull(),
917
date: timestamp("date", { mode: "date" }).notNull(),
1018
time: varchar("time", { length: 50 }),
11-
image: varchar("image", { length: 500 }).notNull(),
19+
image: bytea("image"), // ✅ now works
1220
createdAt: timestamp("created_at").defaultNow().notNull(),
1321
updatedAt: timestamp("updated_at").defaultNow().notNull(),
1422
});

0 commit comments

Comments
 (0)