Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ on:
push:
branches:
- main
pull_request:
branches:
- main

jobs:
build:
Expand All @@ -22,6 +25,9 @@ jobs:
- name: Lint project
run: bun run check && bun run type-check

- name: Check unused code
run: bun run knip

- name: Run tests
run: bun run test

Expand Down
2 changes: 1 addition & 1 deletion .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1 +1 @@
bun run lint-staged && bun run type-check
bun run lint-staged && bun run type-check && bun run knip
2 changes: 1 addition & 1 deletion biome.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"$schema": "https://biomejs.dev/schemas/2.4.7/schema.json",
"$schema": "https://biomejs.dev/schemas/2.4.13/schema.json",
"vcs": {
"enabled": false,
"clientKind": "git",
Expand Down
1,130 changes: 567 additions & 563 deletions bun.lock

Large diffs are not rendered by default.

21 changes: 21 additions & 0 deletions knip.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"$schema": "https://unpkg.com/knip@6/schema.json",
"tags": [
"-lintignore"
],
"ignore": [
"scripts/utils/*",
".agents/hooks/*",
"src/components/ui/*",
"src/lib/trpc/*",
".opencode/plugins/lint.ts",
"src/lib/aws/ses.tsx",
"src/lib/metadata.ts",
"src/lib/stripe.ts",
"scripts/removers/remover.ts"
],
"ignoreDependencies": [
"@opencode-ai/plugin",
"shadcn"
]
}
6 changes: 5 additions & 1 deletion next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ import NextBundleAnalyzer from '@next/bundle-analyzer';
/** @type {import('next').NextConfig} */
const nextConfig = {
// Use `bun run build:standalone` if you are using Docker
output: process.env.EXPORT_MODE
output: process.env.EXPORT_MODE,
turbopack: {
root: import.meta.dirname,
},
reactCompiler: true,
};

const withBundleAnalyzer = NextBundleAnalyzer({
Expand Down
86 changes: 43 additions & 43 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
"version": "0.1.0",
"private": true,
"engines": {
"node": ">=22.20.0",
"bun": ">=1.3.0"
"node": "24.15.0",
"bun": "1.3.13"
},
"packageManager": "bun@1.3.13",
"scripts": {
"dev": "next dev",
"scan": "REACT_SCAN=true next dev",
Expand All @@ -29,44 +30,40 @@
"prepare": "husky",
"postinstall": "prisma generate",
"db:migrate": "prisma migrate dev",
"db:deploy": "prisma migrate deploy"
"db:deploy": "prisma migrate deploy",
"knip": "knip"
},
"lint-staged": {
"*.{ts,tsx}": [
"bun run check"
]
},
"dependencies": {
"@aws-sdk/client-ses": "3.1009.0",
"@aws-sdk/client-ses": "3.1038.0",
"@hookform/resolvers": "5.2.2",
"@marsidev/react-turnstile": "1.4.2",
"@marsidev/react-turnstile": "1.5.1",
"@microsoft/clarity": "1.0.2",
"@next/third-parties": "16.1.6",
"@prisma/adapter-pg": "7.5.0",
"@prisma/client": "7.5.0",
"@react-email/components": "1.0.9",
"@tanstack/query-async-storage-persister": "5.90.24",
"@tanstack/query-sync-storage-persister": "5.90.24",
"@tanstack/react-query": "5.90.21",
"@tanstack/react-query-persist-client": "5.90.24",
"@trpc/client": "11.13.2",
"@trpc/react-query": "11.13.2",
"@trpc/server": "11.13.2",
"@trpc/tanstack-react-query": "11.13.2",
"better-auth": "1.5.5",
"@prisma/adapter-pg": "7.8.0",
"@prisma/client": "7.8.0",
"@react-email/components": "1.0.12",
"@tanstack/react-query": "5.100.6",
"@tanstack/react-query-persist-client": "5.100.6",
"@trpc/client": "11.17.0",
"@trpc/server": "11.17.0",
"@trpc/tanstack-react-query": "11.17.0",
"better-auth": "1.6.9",
"class-variance-authority": "0.7.1",
"clsx": "2.1.1",
"idb-keyval": "6.2.2",
"ioredis": "5.10.0",
"ioredis": "5.10.1",
"javascript-time-ago": "2.6.4",
"lucide-react": "0.577.0",
"motion": "12.36.0",
"next": "16.1.6",
"lucide-react": "1.12.0",
"next": "16.2.4",
"next-themes": "0.4.6",
"radix-ui": "1.4.3",
"react": "19.2.4",
"react-dom": "19.2.4",
"react-hook-form": "7.71.2",
"react": "19.2.5",
"react-dom": "19.2.5",
"react-hook-form": "7.74.0",
"server-only": "0.0.1",
"sonner": "2.0.7",
"stripe": "20.0.0",
Expand All @@ -78,31 +75,34 @@
"zod": "4.3.6"
},
"devDependencies": {
"@biomejs/biome": "2.4.7",
"@commitlint/cli": "20.4.4",
"@commitlint/config-conventional": "20.4.4",
"@next/bundle-analyzer": "16.1.6",
"@opencode-ai/plugin": "1.2.26",
"@react-email/preview-server": "5.2.9",
"@tailwindcss/postcss": "4.2.1",
"@types/bun": "1.3.10",
"@types/node": "25.5.0",
"@biomejs/biome": "2.4.13",
"@commitlint/cli": "20.5.2",
"@commitlint/config-conventional": "20.5.0",
"@next/bundle-analyzer": "16.2.4",
"@opencode-ai/plugin": "1.14.28",
"@react-email/preview-server": "5.2.11",
"@tailwindcss/postcss": "4.2.4",
"@types/bun": "1.3.13",
"@types/node": "24.12.2",
"@types/prompts": "2.4.9",
"@types/react": "19.2.14",
"@types/react-dom": "19.2.3",
"babel-plugin-react-compiler": "1.0.0",
"commander": "14.0.3",
"dotenv": "17.3.1",
"dotenv-expand": "12.0.3",
"dotenv": "17.4.2",
"dotenv-expand": "13.0.0",
"husky": "9.1.7",
"knip": "6.7.0",
"lint-staged": "16.4.0",
"npm-check-updates": "19.6.3",
"postcss": "8.5.8",
"prisma": "7.5.0",
"npm-check-updates": "22.0.1",
"postcss": "8.5.12",
"postcss-load-config": "6.0.1",
"prisma": "7.8.0",
"prompts": "2.4.2",
"react-email": "5.2.9",
"shadcn": "4.0.8",
"tailwindcss": "4.2.1",
"typescript": "5.9.3"
"react-email": "5.2.11",
"shadcn": "4.6.0",
"tailwindcss": "4.2.4",
"typescript": "6.0.3"
},
"overrides": {
"@types/react": "19.2.14",
Expand Down
38 changes: 0 additions & 38 deletions src/components/auth/signout-button.tsx

This file was deleted.

30 changes: 17 additions & 13 deletions src/components/notifications/notification-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,34 @@ import { Bell } from "lucide-react";
import MarkAllAsReadButton from "@/components/notifications/mark-all-as-read-button";
import { NotificationItem } from "@/components/notifications/notification-item";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { buttonVariants } from "@/components/ui/button";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Separator } from "@/components/ui/separator";
import type { Notification } from "@/lib/prisma/generated/client";
import { getUserNotifications } from "@/lib/services/notification.service";
import { cn } from "@/lib/utils";

export default async function NotificationMenu() {
const notifications = await getUserNotifications();

return (
<Popover>
<PopoverTrigger asChild>
<Button variant="ghost" size="icon" className="relative rounded-full">
<Bell size={22} />
{notifications.length > 0 && (
<Badge
variant="destructive"
className="absolute -top-0.5 -right-0.5 inline-flex size-4 items-center justify-center p-0 text-[10px]"
>
{notifications.length > 9 ? "9+" : notifications.length}
</Badge>
)}
</Button>
<PopoverTrigger
className={cn(
buttonVariants({ variant: "ghost", size: "icon" }),
"relative rounded-full"
)}
>
<Bell size={22} />
{notifications.length > 0 && (
Comment on lines +18 to +25

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add an accessible name to the trigger button (Line 18).

When unread count is 0, this becomes effectively icon-only and may have no clear accessible name for screen readers.

♿ Suggested fix
       <PopoverTrigger
+        aria-label={
+          notifications.length > 0
+            ? `Open notifications (${notifications.length} unread)`
+            : "Open notifications"
+        }
         className={cn(
           buttonVariants({ variant: "ghost", size: "icon" }),
           "relative rounded-full"
         )}
       >
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<PopoverTrigger
className={cn(
buttonVariants({ variant: "ghost", size: "icon" }),
"relative rounded-full"
)}
>
<Bell size={22} />
{notifications.length > 0 && (
<PopoverTrigger
aria-label={
notifications.length > 0
? `Open notifications (${notifications.length} unread)`
: "Open notifications"
}
className={cn(
buttonVariants({ variant: "ghost", size: "icon" }),
"relative rounded-full"
)}
>
<Bell size={22} />
{notifications.length > 0 && (
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/notifications/notification-menu.tsx` around lines 18 - 25, The
PopoverTrigger (containing the Bell icon) lacks an accessible name when
notifications.length === 0; add an aria-label (or aria-labelledby) prop on
PopoverTrigger to provide a clear name (e.g., "Notifications" and optionally
include the unread count when notifications.length > 0). Update the
PopoverTrigger element in notification-menu.tsx (the element wrapping Bell) to
always supply this accessible name so screen readers can announce the control
even when the visual label is absent.

<Badge
variant="destructive"
className="absolute -top-0.5 -right-0.5 inline-flex size-4 items-center justify-center p-0 text-[10px]"
>
{notifications.length > 9 ? "9+" : notifications.length}
</Badge>
)}
</PopoverTrigger>
<PopoverContent className="mr-4 flex w-96 flex-col gap-2 py-2 pr-0 pl-2">
<p className="pt-2 pl-4 font-semibold text-xl">Notifications</p>
Expand Down
1 change: 1 addition & 0 deletions src/components/ui/sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ function SidebarProvider({
}

// This sets the cookie to keep the sidebar state.
// biome-ignore lint/suspicious/noDocumentCookie: We need to set a cookie to keep the sidebar state.
document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
},
[setOpenProp, open]
Expand Down
3 changes: 1 addition & 2 deletions src/lib/auth-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,4 @@

import { createAuthClient } from "better-auth/react";

export const { signIn, signUp, signOut, useSession, changePassword, resetPassword } =
createAuthClient();
export const { signIn, signOut, changePassword, resetPassword } = createAuthClient();
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
"./src/*"
]
},
"target": "ES2017"
"target": "es2022",
"types": ["bun"]
},
"include": [
"next-env.d.ts",
Expand Down