what hapenned with the css of this project??? looks awfull

This commit is contained in:
Leon Serfaty G
2025-12-26 00:08:44 +00:00
parent c5ebbc5f77
commit 76ba003c92
12 changed files with 148 additions and 130 deletions
BIN
View File
Binary file not shown.
+3 -3
View File
@@ -1,6 +1,6 @@
import type {NextConfig} from 'next';
const nextConfig: NextConfig = { /** @type {import('next').NextConfig} */
const nextConfig = {
/* config options here */ /* config options here */
output: 'standalone', output: 'standalone',
typescript: { typescript: {
@@ -21,4 +21,4 @@ const nextConfig: NextConfig = {
}, },
}; };
export default nextConfig; module.exports = nextConfig;
+46 -48
View File
@@ -1,4 +1,3 @@
{ {
"name": "nextn", "name": "nextn",
"version": "0.1.0", "version": "0.1.0",
@@ -14,64 +13,63 @@
"db:seed": "tsx scripts/seed.ts" "db:seed": "tsx scripts/seed.ts"
}, },
"dependencies": { "dependencies": {
"@next-auth/better-sqlite3-adapter": "^0.3.1",
"@genkit-ai/googleai": "^1.14.1", "@genkit-ai/googleai": "^1.14.1",
"@genkit-ai/next": "^1.14.1", "@hookform/resolvers": "^3.3.4",
"@hookform/resolvers": "^4.1.3", "@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-accordion": "^1.2.3", "@radix-ui/react-alert-dialog": "^1.0.5",
"@radix-ui/react-alert-dialog": "^1.1.6", "@radix-ui/react-avatar": "^1.0.4",
"@radix-ui/react-avatar": "^1.1.3", "@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-checkbox": "^1.1.4", "@radix-ui/react-collapsible": "^1.0.3",
"@radix-ui/react-collapsible": "^1.1.11", "@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-dialog": "^1.1.6", "@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-dropdown-menu": "^2.1.6", "@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-label": "^2.1.2", "@radix-ui/react-menubar": "^1.0.4",
"@radix-ui/react-menubar": "^1.1.6", "@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-popover": "^1.1.6", "@radix-ui/react-progress": "^1.0.3",
"@radix-ui/react-progress": "^1.1.2", "@radix-ui/react-radio-group": "^1.1.3",
"@radix-ui/react-radio-group": "^1.2.3", "@radix-ui/react-scroll-area": "^1.0.5",
"@radix-ui/react-scroll-area": "^1.2.3", "@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-select": "^2.1.6", "@radix-ui/react-separator": "^1.0.3",
"@radix-ui/react-separator": "^1.1.2", "@radix-ui/react-slider": "^1.1.2",
"@radix-ui/react-slider": "^1.2.3", "@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-switch": "^1.0.3",
"@radix-ui/react-switch": "^1.1.3", "@radix-ui/react-tabs": "^1.0.4",
"@radix-ui/react-tabs": "^1.1.3", "@radix-ui/react-toast": "^1.1.5",
"@radix-ui/react-toast": "^1.2.6", "@radix-ui/react-tooltip": "^1.0.7",
"@radix-ui/react-tooltip": "^1.1.8", "better-sqlite3": "^9.4.3",
"better-sqlite3": "^11.1.2", "class-variance-authority": "^0.7.0",
"class-variance-authority": "^0.7.1", "clsx": "^2.1.0",
"clsx": "^2.1.1",
"date-fns": "^3.6.0", "date-fns": "^3.6.0",
"dotenv": "^16.5.0", "dotenv": "^16.4.5",
"embla-carousel-react": "^8.6.0", "embla-carousel-react": "^8.0.0",
"firebase": "^11.9.1", "firebase": "^10.9.0",
"framer-motion": "^11.3.12", "framer-motion": "^11.0.20",
"genkit": "^1.14.1", "genkit": "latest",
"lucide-react": "^0.475.0", "lucide-react": "^0.359.0",
"next": "15.3.3", "next": "14.2.35",
"next-auth": "5.0.0-beta.19", "next-auth": "4.24.7",
"nodemailer": "^6.9.14", "nodemailer": "^6.9.13",
"patch-package": "^8.0.0",
"pdf-lib": "^1.17.1", "pdf-lib": "^1.17.1",
"react": "^18.3.1", "react": "^18.2.0",
"react-day-picker": "^8.10.1", "react-day-picker": "^8.10.0",
"react-dom": "^18.3.1", "react-dom": "^18.2.0",
"react-hook-form": "^7.54.2", "react-hook-form": "^7.51.1",
"recharts": "^2.15.1", "recharts": "^2.12.3",
"tailwind-merge": "^3.0.1", "tailwind-merge": "^2.2.2",
"tailwindcss-animate": "^1.0.7", "tailwindcss-animate": "^1.0.7",
"zod": "^3.24.2" "zod": "^3.22.4"
}, },
"devDependencies": { "devDependencies": {
"@types/better-sqlite3": "^7.6.11", "@types/better-sqlite3": "^7.6.9",
"@types/node": "^20", "@types/node": "^20",
"@types/nodemailer": "^6.4.15", "@types/nodemailer": "^6.4.14",
"@types/react": "^18", "@types/react": "^18",
"@types/react-dom": "^18", "@types/react-dom": "^18",
"genkit-cli": "^1.14.1", "genkit-cli": "latest",
"postcss": "^8", "postcss": "^8",
"tailwindcss": "^3.4.1", "tailwindcss": "^3.4.1",
"tsx": "^4.16.2", "tsx": "^4.7.1",
"typescript": "^5" "typescript": "^5"
} }
} }
-1
View File
@@ -3,5 +3,4 @@ import {googleAI} from '@genkit-ai/googleai';
export const ai = genkit({ export const ai = genkit({
plugins: [googleAI()], plugins: [googleAI()],
model: 'googleai/gemini-2.0-flash',
}); });
+2 -3
View File
@@ -1,5 +1,5 @@
import { auth, signOut } from "@/auth" import { auth } from "@/auth"
import Link from 'next/link'; import Link from 'next/link';
import { import {
Sidebar, Sidebar,
@@ -26,6 +26,7 @@ import {
} from "lucide-react" } from "lucide-react"
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { logout } from "@/lib/actions/auth"; import { logout } from "@/lib/actions/auth";
import { redirect } from "next/navigation";
async function AdminLayout({ async function AdminLayout({
children, children,
@@ -35,8 +36,6 @@ async function AdminLayout({
const session = await auth() const session = await auth()
if (!session) { if (!session) {
// This should be handled by middleware, but as a fallback
const { redirect } = await import("next/navigation")
redirect("/auth") redirect("/auth")
} }
+6 -2
View File
@@ -1,3 +1,7 @@
import { handlers } from '@/auth';
export const { GET, POST } = handlers; import { authOptions } from "@/auth";
import NextAuth from "next-auth";
const handler = NextAuth(authOptions);
export { handler as GET, handler as POST }
+36 -18
View File
@@ -4,26 +4,35 @@
@layer base { @layer base {
:root { :root {
--background: 222 47% 11%; --background: 0 0% 100%;
--foreground: 0 0% 98%; --foreground: 222.2 84% 4.9%;
--card: 224 35% 15%; --card: 0 0% 100%;
--card-foreground: 0 0% 98%; --card-foreground: 222.2 84% 4.9%;
--popover: 222 47% 11%; --popover: 0 0% 100%;
--popover-foreground: 0 0% 98%; --popover-foreground: 222.2 84% 4.9%;
--primary: 221 100% 58%; --primary: 221 83% 53%;
--primary-foreground: 0 0% 9%; --primary-foreground: 210 40% 98%;
--secondary: 222 25% 20%; --secondary: 210 40% 96.1%;
--secondary-foreground: 0 0% 98%; --secondary-foreground: 222.2 47.4% 11.2%;
--muted: 222 25% 20%; --muted: 210 40% 96.1%;
--muted-foreground: 222 10% 63.9%; --muted-foreground: 215.4 16.3% 46.9%;
--accent: 263 47% 53%; --accent: 210 40% 96.1%;
--accent-foreground: 0 0% 98%; --accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 84.2% 60.2%; --destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%; --destructive-foreground: 210 40% 98%;
--border: 224 35% 20%; --border: 214.3 31.8% 91.4%;
--input: 222 25% 20%; --input: 214.3 31.8% 91.4%;
--ring: 221 100% 58%; --ring: 222.2 84% 4.9%;
--radius: 0.5rem; --radius: 0.5rem;
--sidebar-background: 222 47% 11%;
--sidebar-foreground: 0 0% 98%;
--sidebar-primary: 221 100% 58%;
--sidebar-primary-foreground: 0 0% 9%;
--sidebar-accent: 222 25% 20%;
--sidebar-accent-foreground: 0 0% 98%;
--sidebar-border: 224 35% 20%;
--sidebar-ring: 221 100% 58%;
} }
.dark { .dark {
@@ -46,6 +55,15 @@
--border: 224 35% 20%; --border: 224 35% 20%;
--input: 222 25% 20%; --input: 222 25% 20%;
--ring: 221 100% 58%; --ring: 221 100% 58%;
--sidebar-background: 222 47% 11%;
--sidebar-foreground: 0 0% 98%;
--sidebar-primary: 221 100% 58%;
--sidebar-primary-foreground: 0 0% 9%;
--sidebar-accent: 222 25% 20%;
--sidebar-accent-foreground: 0 0% 98%;
--sidebar-border: 224 35% 20%;
--sidebar-ring: 221 100% 58%;
} }
} }
-22
View File
@@ -5,26 +5,4 @@ export const authConfig = {
pages: { pages: {
signIn: '/auth', signIn: '/auth',
}, },
providers: [
// The Credentials provider logic has been moved to src/auth.ts
// to prevent the database module from being bundled with middleware.
],
callbacks: {
authorized({ auth, request: { nextUrl } }) {
const isLoggedIn = !!auth?.user;
const isOnAdmin = nextUrl.pathname.startsWith('/admin');
if (isOnAdmin) {
return isLoggedIn;
} else if (isLoggedIn) {
// Redirect logged-in users from the login page to the admin dashboard
if (nextUrl.pathname === '/auth') {
return Response.redirect(new URL('/admin', nextUrl));
}
return true;
}
return true;
},
},
} satisfies NextAuthConfig; } satisfies NextAuthConfig;
+40 -12
View File
@@ -1,14 +1,22 @@
import NextAuth from 'next-auth'; import NextAuth, { NextAuthOptions } from 'next-auth';
import { authConfig } from './auth.config'; import CredentialsProvider from 'next-auth/providers/credentials';
import Credentials from 'next-auth/providers/credentials';
import { z } from 'zod'; import { z } from 'zod';
import { getUserByEmail } from '@/lib/actions/user'; import { getUserByEmail } from '@/lib/actions/user';
import getDb from './lib/db';
import { BetterSqlite3Adapter } from '@next-auth/better-sqlite3-adapter';
export const { handlers, auth, signIn, signOut } = NextAuth({ const db = getDb();
...authConfig,
export const authOptions: NextAuthOptions = {
adapter: BetterSqlite3Adapter(db),
providers: [ providers: [
Credentials({ CredentialsProvider({
name: 'credentials',
credentials: {
email: { label: 'email', type: 'text' },
password: { label: 'password', type: 'password' },
},
async authorize(credentials) { async authorize(credentials) {
const parsedCredentials = z const parsedCredentials = z
.object({ email: z.string().email(), password: z.string().min(1) }) .object({ email: z.string().email(), password: z.string().min(1) })
@@ -20,13 +28,11 @@ export const { handlers, auth, signIn, signOut } = NextAuth({
const user = await getUserByEmail(email); const user = await getUserByEmail(email);
if (!user || !user.password) return null; if (!user || !user.password) return null;
// WARNING: Storing passwords in plaintext is insecure. // This is a temporary solution for the demo.
// This is for demonstration purposes only. // In a real application, you should hash and compare passwords securely.
// In a real application, you MUST hash and salt passwords.
const passwordsMatch = password === user.password; const passwordsMatch = password === user.password;
if (passwordsMatch) { if (passwordsMatch) {
// The user object returned here will be encoded in the JWT.
return { id: user.id, name: user.name, email: user.email }; return { id: user.id, name: user.name, email: user.email };
} }
} }
@@ -35,5 +41,27 @@ export const { handlers, auth, signIn, signOut } = NextAuth({
return null; return null;
}, },
}), }),
] ],
}); pages: {
signIn: '/auth',
},
session: {
strategy: 'jwt',
},
callbacks: {
jwt({ token, user }) {
if (user) {
token.id = user.id;
}
return token;
},
session({ session, token }) {
if (session.user) {
session.user.id = token.id as string;
}
return session;
}
}
};
export const { handlers, auth, signIn, signOut } = NextAuth(authOptions);
+6 -9
View File
@@ -1,26 +1,23 @@
'use server'; 'use server';
import { signIn, signOut } from '@/auth'; import { signIn, signOut } from '@/auth';
import { AuthError } from 'next-auth';
export async function login( export async function login(
prevState: { message: string } | undefined, prevState: { message: string } | undefined,
formData: FormData formData: FormData
) { ) {
try { try {
await signIn('credentials', formData); await signIn('credentials', Object.fromEntries(formData));
return { message: "Successfully signed in." }
} catch (error) { } catch (error) {
if (error instanceof AuthError) { console.error("Login error:", error);
switch (error.type) { if ((error as Error).message.includes('CredentialsSignin')) {
case 'CredentialsSignin':
return { message: 'Invalid credentials.' }; return { message: 'Invalid credentials.' };
default: }
return { message: 'Something went wrong. Please try again.' }; return { message: 'Something went wrong. Please try again.' };
} }
} }
throw error;
}
}
export async function logout() { export async function logout() {
+1 -1
View File
@@ -77,7 +77,7 @@ export async function updateUser(data: UserFormValues): Promise<{ success: boole
return { success: false, message: 'This email is already taken.' }; return { success: false, message: 'This email is already taken.' };
} }
if (password) { if (password && password.length > 0) {
// If a new password is provided, update it along with name and email // If a new password is provided, update it along with name and email
const stmt = db.prepare('UPDATE users SET name = ?, email = ?, password = ? WHERE id = ?'); const stmt = db.prepare('UPDATE users SET name = ?, email = ?, password = ? WHERE id = ?');
// In a real app, hash the password! For this example, we store it as plain text. // In a real app, hash the password! For this example, we store it as plain text.
+2 -5
View File
@@ -1,10 +1,7 @@
import NextAuth from 'next-auth'; export { default } from "next-auth/middleware"
import { authConfig } from '@/auth.config';
export default NextAuth(authConfig).auth;
export const config = { export const config = {
// https://nextjs.org/docs/app/building-your-application/routing/middleware#matcher // https://nextjs.org/docs/app/building-your-application/routing/middleware#matcher
matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'], matcher: ['/admin/:path*'],
}; };