mirror of
https://github.com/soconnor0919/hristudio.git
synced 2025-12-11 22:54:45 -05:00
create database connection
This commit is contained in:
11
.env
11
.env
@@ -2,12 +2,15 @@
|
|||||||
# should be updated accordingly.
|
# should be updated accordingly.
|
||||||
|
|
||||||
# Drizzle
|
# Drizzle
|
||||||
DATABASE_URL="postgresql://postgres:password@localhost:5432/hristudio"
|
DATABASE_URL="postgresql://postgres:jusxah-jufrew-niwjY5@db:5432/hristudio"
|
||||||
|
|
||||||
|
# Clerk
|
||||||
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_cmVmaW5lZC1kcnVtLTIzLmNsZXJrLmFjY291bnRzLmRldiQ
|
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_cmVmaW5lZC1kcnVtLTIzLmNsZXJrLmFjY291bnRzLmRldiQ
|
||||||
CLERK_SECRET_KEY=sk_test_3qESERGxZqHpROHzFe7nYxjfqfVhpHWS1UVDQt86v8
|
CLERK_SECRET_KEY=sk_test_3qESERGxZqHpROHzFe7nYxjfqfVhpHWS1UVDQt86v8
|
||||||
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
|
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
|
||||||
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
|
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
|
||||||
# Example:
|
|
||||||
# SERVERVAR="foo"
|
# Database
|
||||||
# NEXT_PUBLIC_CLIENTVAR="bar"
|
POSTGRES_USER=postgres
|
||||||
|
POSTGRES_PASSWORD=jusxah-jufrew-niwjY5
|
||||||
|
POSTGRES_DB=hristudio
|
||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -53,4 +53,7 @@ psd
|
|||||||
thumb
|
thumb
|
||||||
sketch
|
sketch
|
||||||
|
|
||||||
|
pnpm-lock.yaml
|
||||||
|
|
||||||
# End of https://www.toptal.com/developers/gitignore/api/nextjs,react
|
# End of https://www.toptal.com/developers/gitignore/api/nextjs,react
|
||||||
|
pnpm-lock.yaml
|
||||||
|
|||||||
34
package.json
34
package.json
@@ -14,7 +14,7 @@
|
|||||||
"start": "next start"
|
"start": "next start"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@clerk/nextjs": "^5.5.2",
|
"@clerk/nextjs": "^5.6.0",
|
||||||
"@radix-ui/react-avatar": "^1.1.0",
|
"@radix-ui/react-avatar": "^1.1.0",
|
||||||
"@radix-ui/react-dialog": "^1.1.1",
|
"@radix-ui/react-dialog": "^1.1.1",
|
||||||
"@radix-ui/react-icons": "^1.3.0",
|
"@radix-ui/react-icons": "^1.3.0",
|
||||||
@@ -25,9 +25,9 @@
|
|||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cn": "^0.1.1",
|
"cn": "^0.1.1",
|
||||||
"drizzle-orm": "^0.33.0",
|
"drizzle-orm": "^0.33.0",
|
||||||
"geist": "^1.3.0",
|
"geist": "^1.3.1",
|
||||||
"lucide-react": "^0.441.0",
|
"lucide-react": "^0.441.0",
|
||||||
"next": "^14.2.4",
|
"next": "^14.2.12",
|
||||||
"next-themes": "^0.3.0",
|
"next-themes": "^0.3.0",
|
||||||
"postgres": "^3.4.4",
|
"postgres": "^3.4.4",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
@@ -35,24 +35,24 @@
|
|||||||
"react-icons": "^5.3.0",
|
"react-icons": "^5.3.0",
|
||||||
"tailwind-merge": "^2.5.2",
|
"tailwind-merge": "^2.5.2",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"zod": "^3.23.3"
|
"zod": "^3.23.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/eslint": "^8.56.10",
|
"@types/eslint": "^8.56.12",
|
||||||
"@types/node": "^20.14.10",
|
"@types/node": "^20.16.5",
|
||||||
"@types/react": "^18.3.3",
|
"@types/react": "^18.3.7",
|
||||||
"@types/react-dom": "^18.3.0",
|
"@types/react-dom": "^18.3.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.1.0",
|
"@typescript-eslint/eslint-plugin": "^8.6.0",
|
||||||
"@typescript-eslint/parser": "^8.1.0",
|
"@typescript-eslint/parser": "^8.6.0",
|
||||||
"drizzle-kit": "^0.24.0",
|
"drizzle-kit": "^0.24.2",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.1",
|
||||||
"eslint-config-next": "^14.2.4",
|
"eslint-config-next": "^14.2.12",
|
||||||
"eslint-plugin-drizzle": "^0.2.3",
|
"eslint-plugin-drizzle": "^0.2.3",
|
||||||
"postcss": "^8.4.39",
|
"postcss": "^8.4.47",
|
||||||
"prettier": "^3.3.2",
|
"prettier": "^3.3.3",
|
||||||
"prettier-plugin-tailwindcss": "^0.6.5",
|
"prettier-plugin-tailwindcss": "^0.6.6",
|
||||||
"tailwindcss": "^3.4.3",
|
"tailwindcss": "^3.4.12",
|
||||||
"typescript": "^5.5.3"
|
"typescript": "^5.6.2"
|
||||||
},
|
},
|
||||||
"ct3aMetadata": {
|
"ct3aMetadata": {
|
||||||
"initVersion": "7.37.0"
|
"initVersion": "7.37.0"
|
||||||
|
|||||||
858
pnpm-lock.yaml
generated
858
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
31
src/app/api/studies/route.ts
Normal file
31
src/app/api/studies/route.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { db } from "~/server/db";
|
||||||
|
import { studies } from "~/server/db/schema";
|
||||||
|
import { NextResponse } from "next/server";
|
||||||
|
import { eq } from "drizzle-orm";
|
||||||
|
|
||||||
|
export async function GET() {
|
||||||
|
const allStudies = await db.select().from(studies);
|
||||||
|
return NextResponse.json(allStudies);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function POST(request: Request) {
|
||||||
|
const { title, description } = await request.json();
|
||||||
|
const newStudy = await db.insert(studies).values({ title, description }).returning();
|
||||||
|
return NextResponse.json(newStudy[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function PUT(request: Request) {
|
||||||
|
const { id, title, description } = await request.json();
|
||||||
|
const updatedStudy = await db
|
||||||
|
.update(studies)
|
||||||
|
.set({ title, description })
|
||||||
|
.where(eq(studies.id, id))
|
||||||
|
.returning();
|
||||||
|
return NextResponse.json(updatedStudy[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function DELETE(request: Request) {
|
||||||
|
const { id } = await request.json();
|
||||||
|
await db.delete(studies).where(eq(studies.id, id));
|
||||||
|
return NextResponse.json({ message: "Study deleted" });
|
||||||
|
}
|
||||||
@@ -1,9 +1,15 @@
|
|||||||
import { type PropsWithChildren } from "react"
|
import { type PropsWithChildren } from "react"
|
||||||
import { Sidebar } from "~/components/sidebar"
|
import { Sidebar } from "~/components/sidebar"
|
||||||
import { inter } from "../layout"
|
import { Inter } from "next/font/google"
|
||||||
|
|
||||||
import "~/styles/globals.css"
|
import "~/styles/globals.css"
|
||||||
|
|
||||||
|
const inter = Inter({
|
||||||
|
subsets: ["latin"],
|
||||||
|
display: "swap",
|
||||||
|
variable: "--font-sans",
|
||||||
|
})
|
||||||
|
|
||||||
export default function RootLayout({ children }: PropsWithChildren) {
|
export default function RootLayout({ children }: PropsWithChildren) {
|
||||||
return (
|
return (
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '~/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '~/components/ui/card';
|
||||||
import { Button } from '~/components/ui/button';
|
import { Button } from '~/components/ui/button';
|
||||||
|
import { Studies } from "~/components/Studies";
|
||||||
|
|
||||||
const HomePage: React.FC = () => {
|
const HomePage: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
@@ -11,41 +12,7 @@ const HomePage: React.FC = () => {
|
|||||||
Manage your Human-Robot Interaction projects and experiments
|
Manage your Human-Robot Interaction projects and experiments
|
||||||
</p>
|
</p>
|
||||||
</header>
|
</header>
|
||||||
|
<Studies />
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
||||||
<Card className="bg-white shadow-lg">
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle className="text-2xl font-semibold text-blue-700">Projects</CardTitle>
|
|
||||||
<CardDescription>Manage your HRI projects</CardDescription>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<p className="mb-4">Create, edit, and analyze your HRI projects.</p>
|
|
||||||
<Button className="bg-blue-600 hover:bg-blue-700 text-white">View Projects</Button>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Card className="bg-white shadow-lg">
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle className="text-2xl font-semibold text-blue-700">Experiments</CardTitle>
|
|
||||||
<CardDescription>Design and run experiments</CardDescription>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<p className="mb-4">Set up, conduct, and analyze HRI experiments.</p>
|
|
||||||
<Button className="bg-green-600 hover:bg-green-700 text-white">New Experiment</Button>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Card className="bg-white shadow-lg">
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle className="text-2xl font-semibold text-blue-700">Data Analysis</CardTitle>
|
|
||||||
<CardDescription>Analyze your research data</CardDescription>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<p className="mb-4">Visualize and interpret your HRI research data.</p>
|
|
||||||
<Button className="bg-purple-600 hover:bg-purple-700 text-white">Analyze Data</Button>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import { ClerkProvider } from '@clerk/nextjs'
|
import { ClerkProvider } from '@clerk/nextjs'
|
||||||
import { Inter } from "next/font/google"
|
import { Inter } from "next/font/google"
|
||||||
import { ThemeProvider } from "next-themes"
|
|
||||||
|
|
||||||
import "~/styles/globals.css"
|
import "~/styles/globals.css"
|
||||||
|
|
||||||
export const inter = Inter({
|
const inter = Inter({
|
||||||
subsets: ["latin"],
|
subsets: ["latin"],
|
||||||
display: "swap",
|
display: "swap",
|
||||||
variable: "--font-sans",
|
variable: "--font-sans",
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ export default function HomePage() {
|
|||||||
<div className="text-center mb-12">
|
<div className="text-center mb-12">
|
||||||
<h2 className="text-3xl font-semibold mb-4 text-blue-700">Join the HRI Revolution</h2>
|
<h2 className="text-3xl font-semibold mb-4 text-blue-700">Join the HRI Revolution</h2>
|
||||||
<p className="text-lg text-gray-700 mb-6">
|
<p className="text-lg text-gray-700 mb-6">
|
||||||
Whether you're a seasoned researcher or just starting in the field of Human-Robot Interaction,
|
Whether you're a seasoned researcher or just starting in the field of Human-Robot Interaction,
|
||||||
HRIStudio provides the tools and support you need to succeed.
|
HRIStudio provides the tools and support you need to succeed.
|
||||||
</p>
|
</p>
|
||||||
<div className="space-x-4">
|
<div className="space-x-4">
|
||||||
|
|||||||
@@ -31,8 +31,9 @@ export default function SignInPage() {
|
|||||||
await setActive({ session: result.createdSessionId })
|
await setActive({ session: result.createdSessionId })
|
||||||
router.push("/dash")
|
router.push("/dash")
|
||||||
}
|
}
|
||||||
} catch (err: any) {
|
} catch (err) {
|
||||||
console.error("Error:", err.errors[0].message)
|
const error = err as { errors?: { message: string }[] };
|
||||||
|
console.error("Error:", error.errors?.[0]?.message ?? "Unknown error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,6 +43,8 @@ export default function SignInPage() {
|
|||||||
strategy,
|
strategy,
|
||||||
redirectUrl: "/sso-callback",
|
redirectUrl: "/sso-callback",
|
||||||
redirectUrlComplete: "/dash",
|
redirectUrlComplete: "/dash",
|
||||||
|
}).catch((error) => {
|
||||||
|
console.error("Authentication error:", error); // Handle any potential errors
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,7 +93,7 @@ export default function SignInPage() {
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
<CardFooter className="flex flex-col">
|
<CardFooter className="flex flex-col">
|
||||||
<p className="mt-4 text-sm text-center">
|
<p className="mt-4 text-sm text-center">
|
||||||
Don't have an account?{" "}
|
Don't have an account?{" "}
|
||||||
<Link href="/sign-up" className="text-blue-600 hover:underline">
|
<Link href="/sign-up" className="text-blue-600 hover:underline">
|
||||||
Sign up
|
Sign up
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -31,8 +31,9 @@ export default function SignUpPage() {
|
|||||||
await setActive({ session: result.createdSessionId })
|
await setActive({ session: result.createdSessionId })
|
||||||
router.push("/dash")
|
router.push("/dash")
|
||||||
}
|
}
|
||||||
} catch (err: any) {
|
} catch (err) {
|
||||||
console.error("Error:", err.errors[0].message)
|
const error = err as { errors?: { message: string }[] }; // Specify type
|
||||||
|
console.error("Error:", error.errors?.[0]?.message ?? "Unknown error") // Use optional chaining
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,6 +43,8 @@ export default function SignUpPage() {
|
|||||||
strategy,
|
strategy,
|
||||||
redirectUrl: "/sso-callback",
|
redirectUrl: "/sso-callback",
|
||||||
redirectUrlComplete: "/dash",
|
redirectUrlComplete: "/dash",
|
||||||
|
}).catch((error) => {
|
||||||
|
console.error("Authentication error:", error); // Handle any potential errors
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
122
src/components/Studies.tsx
Normal file
122
src/components/Studies.tsx
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card";
|
||||||
|
import { Button } from "~/components/ui/button";
|
||||||
|
import { Input } from "~/components/ui/input";
|
||||||
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "~/components/ui/dialog";
|
||||||
|
|
||||||
|
interface Study {
|
||||||
|
id: number;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Studies() {
|
||||||
|
const [studies, setStudies] = useState<Study[]>([]);
|
||||||
|
const [newStudy, setNewStudy] = useState({ title: '', description: '' });
|
||||||
|
const [editingStudy, setEditingStudy] = useState<Study | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchStudies();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const fetchStudies = async () => {
|
||||||
|
const response = await fetch('/api/studies');
|
||||||
|
const data = await response.json();
|
||||||
|
setStudies(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
const createStudy = async () => {
|
||||||
|
const response = await fetch('/api/studies', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(newStudy),
|
||||||
|
});
|
||||||
|
const createdStudy = await response.json();
|
||||||
|
setStudies([...studies, createdStudy]);
|
||||||
|
setNewStudy({ title: '', description: '' });
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateStudy = async () => {
|
||||||
|
if (!editingStudy) return;
|
||||||
|
const response = await fetch('/api/studies', {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(editingStudy),
|
||||||
|
});
|
||||||
|
const updatedStudy = await response.json();
|
||||||
|
setStudies(studies.map(s => s.id === updatedStudy.id ? updatedStudy : s));
|
||||||
|
setEditingStudy(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteStudy = async (id: number) => {
|
||||||
|
await fetch('/api/studies', {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ id }),
|
||||||
|
});
|
||||||
|
setStudies(studies.filter(s => s.id !== id));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-4">
|
||||||
|
<h2 className="text-2xl font-bold">Studies</h2>
|
||||||
|
<Dialog>
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
<Button>Create New Study</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Create New Study</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<Input
|
||||||
|
placeholder="Title"
|
||||||
|
value={newStudy.title}
|
||||||
|
onChange={(e) => setNewStudy({ ...newStudy, title: e.target.value })}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
placeholder="Description"
|
||||||
|
value={newStudy.description}
|
||||||
|
onChange={(e) => setNewStudy({ ...newStudy, description: e.target.value })}
|
||||||
|
/>
|
||||||
|
<Button onClick={createStudy}>Create</Button>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
{studies.map((study) => (
|
||||||
|
<Card key={study.id}>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>{study.title}</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<p>{study.description}</p>
|
||||||
|
<div className="flex space-x-2 mt-2">
|
||||||
|
<Dialog>
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
<Button variant="outline" onClick={() => setEditingStudy(study)}>Edit</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Edit Study</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<Input
|
||||||
|
placeholder="Title"
|
||||||
|
value={editingStudy?.title || ''}
|
||||||
|
onChange={(e) => setEditingStudy({ ...editingStudy!, title: e.target.value })}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
placeholder="Description"
|
||||||
|
value={editingStudy?.description || ''}
|
||||||
|
onChange={(e) => setEditingStudy({ ...editingStudy!, description: e.target.value })}
|
||||||
|
/>
|
||||||
|
<Button onClick={updateStudy}>Update</Button>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
<Button variant="destructive" onClick={() => deleteStudy(study.id)}>Delete</Button>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -61,8 +61,8 @@ export function Sidebar() {
|
|||||||
<div className="flex items-center space-x-4">
|
<div className="flex items-center space-x-4">
|
||||||
<UserButton />
|
<UserButton />
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-medium text-blue-800">{user?.fullName || 'User'}</p>
|
<p className="text-sm font-medium text-blue-800">{user?.fullName ?? 'User'}</p>
|
||||||
<p className="text-xs text-blue-600">{user?.primaryEmailAddress?.emailAddress || 'user@example.com'}</p>
|
<p className="text-xs text-blue-600">{user?.primaryEmailAddress?.emailAddress ?? 'user@example.com'}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,10 +2,8 @@ import * as React from "react"
|
|||||||
|
|
||||||
import { cn } from "~/lib/utils"
|
import { cn } from "~/lib/utils"
|
||||||
|
|
||||||
export interface InputProps
|
// Use React.InputHTMLAttributes<HTMLInputElement> directly in the component
|
||||||
extends React.InputHTMLAttributes<HTMLInputElement> {}
|
const Input = React.forwardRef<HTMLInputElement, React.InputHTMLAttributes<HTMLInputElement>>(
|
||||||
|
|
||||||
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
||||||
({ className, type, ...props }, ref) => {
|
({ className, type, ...props }, ref) => {
|
||||||
return (
|
return (
|
||||||
<input
|
<input
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
// Example model schema from the Drizzle docs
|
// Example model schema from the Drizzle docs
|
||||||
// https://orm.drizzle.team/docs/sql-schema-declaration
|
// https://orm.drizzle.team/docs/sql-schema-declaration
|
||||||
|
|
||||||
import { sql } from "drizzle-orm";
|
import { pgTableCreator } from "drizzle-orm/pg-core";
|
||||||
|
import { ColumnBaseConfig, ColumnDataType, SQL, sql } from "drizzle-orm";
|
||||||
import {
|
import {
|
||||||
index,
|
index,
|
||||||
pgTableCreator,
|
pgTable,
|
||||||
serial,
|
serial,
|
||||||
|
integer,
|
||||||
timestamp,
|
timestamp,
|
||||||
varchar,
|
varchar,
|
||||||
|
ExtraConfigColumn,
|
||||||
} from "drizzle-orm/pg-core";
|
} from "drizzle-orm/pg-core";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -30,7 +33,25 @@ export const posts = createTable(
|
|||||||
() => new Date()
|
() => new Date()
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
(example) => ({
|
(example: { name: SQL<unknown> | Partial<ExtraConfigColumn<ColumnBaseConfig<ColumnDataType, string>>>; }) => ({
|
||||||
nameIndex: index("name_idx").on(example.name),
|
nameIndex: index("name_idx").on(example.name),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const studies = createTable(
|
||||||
|
"study",
|
||||||
|
{
|
||||||
|
id: serial("id").primaryKey(),
|
||||||
|
title: varchar("title", { length: 256 }).notNull(),
|
||||||
|
description: varchar("description", { length: 1000 }),
|
||||||
|
createdAt: timestamp("created_at", { withTimezone: true })
|
||||||
|
.default(sql`CURRENT_TIMESTAMP`)
|
||||||
|
.notNull(),
|
||||||
|
updatedAt: timestamp("updated_at", { withTimezone: true }).$onUpdate(
|
||||||
|
() => new Date()
|
||||||
|
),
|
||||||
|
},
|
||||||
|
(study: { title: SQL<unknown> | Partial<ExtraConfigColumn<ColumnBaseConfig<ColumnDataType, string>>>; }) => ({
|
||||||
|
titleIndex: index("title_idx").on(study.title),
|
||||||
|
})
|
||||||
|
);
|
||||||
Reference in New Issue
Block a user