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