diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..0db520d
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,48 @@
+name: CI
+
+on:
+ push:
+ branches:
+ - main
+ - dev
+ pull_request:
+ branches:
+ - main
+ - dev
+
+jobs:
+ typecheck:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 22
+ cache: npm
+ - run: npm ci --ignore-scripts
+ - run: npx prisma generate
+ env:
+ DIRECT_URL: "postgresql://placeholder"
+ - run: npx tsc --noEmit
+
+ lint:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 22
+ cache: npm
+ - run: npm ci --ignore-scripts
+ - run: npm run lint
+
+ format:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 22
+ cache: npm
+ - run: npm ci --ignore-scripts
+ - run: npm run format:check
diff --git a/README.md b/README.md
index 692609b..ee5c16d 100644
--- a/README.md
+++ b/README.md
@@ -39,13 +39,13 @@ Internal authentication service
cp .env.example .env
```
- | Variable | Purpose |
- | --- | --- |
- | `DATABASE_URL` | Pooled Postgres connection used by the application (via PgBouncer in prod) |
- | `DIRECT_URL` | Direct Postgres connection used by Prisma for migrations |
- | `NEXT_PUBLIC_SUPABASE_URL` | Supabase project URL (public) |
- | `NEXT_PUBLIC_SUPABASE_ANON_KEY` | Supabase anon key — "Publishable" in CLI output (public) |
- | `SUPABASE_SERVICE_ROLE_KEY` | Supabase service role key — "Secret" in CLI output (**never expose to the client**) |
+ | Variable | Purpose |
+ | ------------------------------- | ----------------------------------------------------------------------------------- |
+ | `DATABASE_URL` | Pooled Postgres connection used by the application (via PgBouncer in prod) |
+ | `DIRECT_URL` | Direct Postgres connection used by Prisma for migrations |
+ | `NEXT_PUBLIC_SUPABASE_URL` | Supabase project URL (public) |
+ | `NEXT_PUBLIC_SUPABASE_ANON_KEY` | Supabase anon key — "Publishable" in CLI output (public) |
+ | `SUPABASE_SERVICE_ROLE_KEY` | Supabase service role key — "Secret" in CLI output (**never expose to the client**) |
> Locally there is no connection pooling, so `DATABASE_URL` and `DIRECT_URL` will be the same.
@@ -63,11 +63,11 @@ Internal authentication service
## Scripts
-| Command | Description |
-| --- | --- |
-| `npm run dev` | Start Next.js dev server |
-| `npm run build` | Production build |
-| `npm run start` | Start production server |
-| `npm run lint` | Run ESLint |
-| `npm run format` | Format code with Prettier |
+| Command | Description |
+| ---------------------- | -------------------------------- |
+| `npm run dev` | Start Next.js dev server |
+| `npm run build` | Production build |
+| `npm run start` | Start production server |
+| `npm run lint` | Run ESLint |
+| `npm run format` | Format code with Prettier |
| `npm run format:check` | Check formatting without writing |
diff --git a/eslint.config.mjs b/eslint.config.mjs
index 863deca..8c31ce7 100644
--- a/eslint.config.mjs
+++ b/eslint.config.mjs
@@ -14,6 +14,8 @@ const eslintConfig = defineConfig([
"out/**",
"build/**",
"next-env.d.ts",
+ // Generated files:
+ "src/generated/**",
]),
]);
diff --git a/package-lock.json b/package-lock.json
index bc507a2..8e10dd3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -37,7 +37,7 @@
"devDependencies": {
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
- "@types/pg": "^8.20.0",
+ "@types/pg": "^8.11.11",
"@types/react": "^19",
"@types/react-dom": "^19",
"eslint": "^9",
@@ -2065,35 +2065,6 @@
"postgres-array": "3.0.4"
}
},
- "node_modules/@prisma/adapter-pg/node_modules/@types/pg": {
- "version": "8.11.11",
- "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.11.11.tgz",
- "integrity": "sha512-kGT1qKM8wJQ5qlawUrEkXgvMSXoV213KfMGXcwfDwUIfUHXqXYXOfS1nE1LINRJVVVx5wCm70XnFlMHaIcQAfw==",
- "license": "MIT",
- "dependencies": {
- "@types/node": "*",
- "pg-protocol": "*",
- "pg-types": "^4.0.1"
- }
- },
- "node_modules/@prisma/adapter-pg/node_modules/pg-types": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-4.1.0.tgz",
- "integrity": "sha512-o2XFanIMy/3+mThw69O8d4n1E5zsLhdO+OPqswezu7Z5ekP4hYDqlDjlmOpYMbzY2Br0ufCwJLdDIXeNVwcWFg==",
- "license": "MIT",
- "dependencies": {
- "pg-int8": "1.0.1",
- "pg-numeric": "1.0.2",
- "postgres-array": "~3.0.1",
- "postgres-bytea": "~3.0.0",
- "postgres-date": "~2.1.0",
- "postgres-interval": "^3.0.0",
- "postgres-range": "^1.1.1"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/@prisma/adapter-pg/node_modules/postgres-array": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.4.tgz",
@@ -2103,36 +2074,6 @@
"node": ">=12"
}
},
- "node_modules/@prisma/adapter-pg/node_modules/postgres-bytea": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-3.0.0.tgz",
- "integrity": "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==",
- "license": "MIT",
- "dependencies": {
- "obuf": "~1.1.2"
- },
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/@prisma/adapter-pg/node_modules/postgres-date": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-2.1.0.tgz",
- "integrity": "sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==",
- "license": "MIT",
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@prisma/adapter-pg/node_modules/postgres-interval": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-3.0.0.tgz",
- "integrity": "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==",
- "license": "MIT",
- "engines": {
- "node": ">=12"
- }
- },
"node_modules/@prisma/client": {
"version": "7.6.0",
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-7.6.0.tgz",
@@ -3140,15 +3081,71 @@
}
},
"node_modules/@types/pg": {
- "version": "8.20.0",
- "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.20.0.tgz",
- "integrity": "sha512-bEPFOaMAHTEP1EzpvHTbmwR8UsFyHSKsRisLIHVMXnpNefSbGA1bD6CVy+qKjGSqmZqNqBDV2azOBo8TgkcVow==",
- "dev": true,
+ "version": "8.11.11",
+ "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.11.11.tgz",
+ "integrity": "sha512-kGT1qKM8wJQ5qlawUrEkXgvMSXoV213KfMGXcwfDwUIfUHXqXYXOfS1nE1LINRJVVVx5wCm70XnFlMHaIcQAfw==",
"license": "MIT",
"dependencies": {
"@types/node": "*",
"pg-protocol": "*",
- "pg-types": "^2.2.0"
+ "pg-types": "^4.0.1"
+ }
+ },
+ "node_modules/@types/pg/node_modules/pg-types": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-4.1.0.tgz",
+ "integrity": "sha512-o2XFanIMy/3+mThw69O8d4n1E5zsLhdO+OPqswezu7Z5ekP4hYDqlDjlmOpYMbzY2Br0ufCwJLdDIXeNVwcWFg==",
+ "license": "MIT",
+ "dependencies": {
+ "pg-int8": "1.0.1",
+ "pg-numeric": "1.0.2",
+ "postgres-array": "~3.0.1",
+ "postgres-bytea": "~3.0.0",
+ "postgres-date": "~2.1.0",
+ "postgres-interval": "^3.0.0",
+ "postgres-range": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@types/pg/node_modules/postgres-array": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.4.tgz",
+ "integrity": "sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@types/pg/node_modules/postgres-bytea": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-3.0.0.tgz",
+ "integrity": "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==",
+ "license": "MIT",
+ "dependencies": {
+ "obuf": "~1.1.2"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/@types/pg/node_modules/postgres-date": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-2.1.0.tgz",
+ "integrity": "sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@types/pg/node_modules/postgres-interval": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-3.0.0.tgz",
+ "integrity": "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
}
},
"node_modules/@types/react": {
diff --git a/package.json b/package.json
index 283b06d..0769854 100644
--- a/package.json
+++ b/package.json
@@ -41,7 +41,7 @@
"devDependencies": {
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
- "@types/pg": "^8.20.0",
+ "@types/pg": "^8.11.11",
"@types/react": "^19",
"@types/react-dom": "^19",
"eslint": "^9",
diff --git a/prisma.config.ts b/prisma.config.ts
index 56a9206..b7e2c04 100644
--- a/prisma.config.ts
+++ b/prisma.config.ts
@@ -7,7 +7,7 @@ export default defineConfig({
schema: "prisma/schema.prisma",
migrations: {
path: "prisma/migrations",
- seed: 'npx ts-node prisma/seed.ts',
+ seed: "npx ts-node prisma/seed.ts",
},
datasource: {
url: env("DIRECT_URL"),
diff --git a/prisma/seed.ts b/prisma/seed.ts
index 39a4fa0..21a6561 100644
--- a/prisma/seed.ts
+++ b/prisma/seed.ts
@@ -1,39 +1,39 @@
-import 'dotenv/config'
-import pg from 'pg'
-import { PrismaPg } from '@prisma/adapter-pg'
-import { PrismaClient } from '../src/generated/prisma/index.js'
+import "dotenv/config";
+import pg from "pg";
+import { PrismaPg } from "@prisma/adapter-pg";
+import { PrismaClient } from "../src/generated/prisma/index.js";
// Connect to whatever database is specified by DATABASE_URL
-const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL })
+const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL });
-const adapter = new PrismaPg(pool as any)
+const adapter = new PrismaPg(pool);
-const prisma = new PrismaClient({ adapter })
+const prisma = new PrismaClient({ adapter });
async function main() {
// Fake Supabase user IDs
- const adminSupabaseId = '00000000-0000-0000-0000-000000000001'
- const userSupabaseId = '00000000-0000-0000-0000-000000000002'
+ const adminSupabaseId = "00000000-0000-0000-0000-000000000001";
+ const userSupabaseId = "00000000-0000-0000-0000-000000000002";
const admin = await prisma.user.upsert({
where: { supabaseUserId: adminSupabaseId },
update: {},
create: { supabaseUserId: adminSupabaseId, isAdmin: true },
- })
+ });
const user = await prisma.user.upsert({
where: { supabaseUserId: userSupabaseId },
update: {},
create: { supabaseUserId: userSupabaseId, isAdmin: false },
- })
+ });
const project1 = await prisma.project.create({
- data: { name: 'Attendance Manager', description: 'Cool project' },
- })
+ data: { name: "Attendance Manager", description: "Cool project" },
+ });
const project2 = await prisma.project.create({
- data: { name: 'Website Creation', description: 'Less project' },
- })
+ data: { name: "Website Creation", description: "Less project" },
+ });
await prisma.userProject.createMany({
data: [
@@ -42,22 +42,22 @@ async function main() {
{ userId: user.id, projectId: project1.id },
],
skipDuplicates: true,
- })
+ });
await prisma.session.create({
data: {
userId: admin.id,
projectId: project1.id,
- token: 'admin-session-token',
+ token: "admin-session-token",
expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24), // 24h
},
- })
+ });
- console.log('data seeded!')
+ console.log("data seeded!");
}
main()
.catch(console.error)
.finally(async () => {
- await prisma.$disconnect()
- })
+ await prisma.$disconnect();
+ });
diff --git a/src/components/ui/badge.tsx b/src/components/ui/badge.tsx
index b20959d..a8cbbac 100644
--- a/src/components/ui/badge.tsx
+++ b/src/components/ui/badge.tsx
@@ -1,8 +1,8 @@
-import { mergeProps } from "@base-ui/react/merge-props"
-import { useRender } from "@base-ui/react/use-render"
-import { cva, type VariantProps } from "class-variance-authority"
+import { mergeProps } from "@base-ui/react/merge-props";
+import { useRender } from "@base-ui/react/use-render";
+import { cva, type VariantProps } from "class-variance-authority";
-import { cn } from "@/lib/utils"
+import { cn } from "@/lib/utils";
const badgeVariants = cva(
"group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!",
@@ -24,8 +24,8 @@ const badgeVariants = cva(
defaultVariants: {
variant: "default",
},
- }
-)
+ },
+);
function Badge({
className,
@@ -39,14 +39,14 @@ function Badge({
{
className: cn(badgeVariants({ variant }), className),
},
- props
+ props,
),
render,
state: {
slot: "badge",
variant,
},
- })
+ });
}
-export { Badge, badgeVariants }
+export { Badge, badgeVariants };
diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx
index 09df753..cc44df6 100644
--- a/src/components/ui/button.tsx
+++ b/src/components/ui/button.tsx
@@ -1,7 +1,7 @@
-import { Button as ButtonPrimitive } from "@base-ui/react/button"
-import { cva, type VariantProps } from "class-variance-authority"
+import { Button as ButtonPrimitive } from "@base-ui/react/button";
+import { cva, type VariantProps } from "class-variance-authority";
-import { cn } from "@/lib/utils"
+import { cn } from "@/lib/utils";
const buttonVariants = cva(
"group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
@@ -37,8 +37,8 @@ const buttonVariants = cva(
variant: "default",
size: "default",
},
- }
-)
+ },
+);
function Button({
className,
@@ -52,7 +52,7 @@ function Button({
className={cn(buttonVariants({ variant, size, className }))}
{...props}
/>
- )
+ );
}
-export { Button, buttonVariants }
+export { Button, buttonVariants };
diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx
index 40cac5f..325aa00 100644
--- a/src/components/ui/card.tsx
+++ b/src/components/ui/card.tsx
@@ -1,6 +1,6 @@
-import * as React from "react"
+import * as React from "react";
-import { cn } from "@/lib/utils"
+import { cn } from "@/lib/utils";
function Card({
className,
@@ -13,11 +13,11 @@ function Card({
data-size={size}
className={cn(
"group/card flex flex-col gap-4 overflow-hidden rounded-xl bg-card py-4 text-sm text-card-foreground ring-1 ring-foreground/10 has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl",
- className
+ className,
)}
{...props}
/>
- )
+ );
}
function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
@@ -26,11 +26,11 @@ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
data-slot="card-header"
className={cn(
"group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl px-4 group-data-[size=sm]/card:px-3 has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3",
- className
+ className,
)}
{...props}
/>
- )
+ );
}
function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
@@ -39,11 +39,11 @@ function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
data-slot="card-title"
className={cn(
"font-heading text-base leading-snug font-medium group-data-[size=sm]/card:text-sm",
- className
+ className,
)}
{...props}
/>
- )
+ );
}
function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
@@ -53,7 +53,7 @@ function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
- )
+ );
}
function CardAction({ className, ...props }: React.ComponentProps<"div">) {
@@ -62,11 +62,11 @@ function CardAction({ className, ...props }: React.ComponentProps<"div">) {
data-slot="card-action"
className={cn(
"col-start-2 row-span-2 row-start-1 self-start justify-self-end",
- className
+ className,
)}
{...props}
/>
- )
+ );
}
function CardContent({ className, ...props }: React.ComponentProps<"div">) {
@@ -76,7 +76,7 @@ function CardContent({ className, ...props }: React.ComponentProps<"div">) {
className={cn("px-4 group-data-[size=sm]/card:px-3", className)}
{...props}
/>
- )
+ );
}
function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
@@ -85,11 +85,11 @@ function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
data-slot="card-footer"
className={cn(
"flex items-center rounded-b-xl border-t bg-muted/50 p-4 group-data-[size=sm]/card:p-3",
- className
+ className,
)}
{...props}
/>
- )
+ );
}
export {
@@ -100,4 +100,4 @@ export {
CardAction,
CardDescription,
CardContent,
-}
+};
diff --git a/src/components/ui/dialog.tsx b/src/components/ui/dialog.tsx
index 014f5aa..308e17f 100644
--- a/src/components/ui/dialog.tsx
+++ b/src/components/ui/dialog.tsx
@@ -1,26 +1,26 @@
-"use client"
+"use client";
-import * as React from "react"
-import { Dialog as DialogPrimitive } from "@base-ui/react/dialog"
+import * as React from "react";
+import { Dialog as DialogPrimitive } from "@base-ui/react/dialog";
-import { cn } from "@/lib/utils"
-import { Button } from "@/components/ui/button"
-import { XIcon } from "lucide-react"
+import { cn } from "@/lib/utils";
+import { Button } from "@/components/ui/button";
+import { XIcon } from "lucide-react";
function Dialog({ ...props }: DialogPrimitive.Root.Props) {
- return
) { className={cn("text-muted-foreground text-sm", className)} {...props} /> - ) + ); } function FormMessage({ className, ...props }: React.ComponentProps<"p">) { - const { error, formMessageId } = useFormField() - const body = error ? String(error?.message ?? "") : props.children + const { error, formMessageId } = useFormField(); + const body = error ? String(error?.message ?? "") : props.children; if (!body) { - return null + return null; } return ( @@ -142,7 +143,7 @@ function FormMessage({ className, ...props }: React.ComponentProps<"p">) { > {body}
- ) + ); } export { @@ -154,4 +155,4 @@ export { FormDescription, FormMessage, FormField, -} +}; diff --git a/src/components/ui/input.tsx b/src/components/ui/input.tsx index 7d21bab..abb87eb 100644 --- a/src/components/ui/input.tsx +++ b/src/components/ui/input.tsx @@ -1,7 +1,7 @@ -import * as React from "react" -import { Input as InputPrimitive } from "@base-ui/react/input" +import * as React from "react"; +import { Input as InputPrimitive } from "@base-ui/react/input"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; function Input({ className, type, ...props }: React.ComponentProps<"input">) { return ( @@ -10,11 +10,11 @@ function Input({ className, type, ...props }: React.ComponentProps<"input">) { data-slot="input" className={cn( "h-8 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40", - className + className, )} {...props} /> - ) + ); } -export { Input } +export { Input }; diff --git a/src/components/ui/label.tsx b/src/components/ui/label.tsx index 74da65c..680d9a6 100644 --- a/src/components/ui/label.tsx +++ b/src/components/ui/label.tsx @@ -1,8 +1,8 @@ -"use client" +"use client"; -import * as React from "react" +import * as React from "react"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; function Label({ className, ...props }: React.ComponentProps<"label">) { return ( @@ -10,11 +10,11 @@ function Label({ className, ...props }: React.ComponentProps<"label">) { data-slot="label" className={cn( "flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50", - className + className, )} {...props} /> - ) + ); } -export { Label } +export { Label }; diff --git a/src/components/ui/sonner.tsx b/src/components/ui/sonner.tsx index 9280ee5..7cbe96b 100644 --- a/src/components/ui/sonner.tsx +++ b/src/components/ui/sonner.tsx @@ -1,32 +1,28 @@ -"use client" +"use client"; -import { useTheme } from "next-themes" -import { Toaster as Sonner, type ToasterProps } from "sonner" -import { CircleCheckIcon, InfoIcon, TriangleAlertIcon, OctagonXIcon, Loader2Icon } from "lucide-react" +import { useTheme } from "next-themes"; +import { Toaster as Sonner, type ToasterProps } from "sonner"; +import { + CircleCheckIcon, + InfoIcon, + TriangleAlertIcon, + OctagonXIcon, + Loader2Icon, +} from "lucide-react"; const Toaster = ({ ...props }: ToasterProps) => { - const { theme = "system" } = useTheme() + const { theme = "system" } = useTheme(); return (