diff --git a/package-lock.json b/package-lock.json index 4a6783f..e9cedd4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,7 @@ "clsx": "^2.1.1", "dotenv": "^16.4.7", "fontsource-roboto": "^4.0.0", - "framer-motion": "^11.11.17", + "framer-motion": "^11.18.0", "lucide-react": "^0.460.0", "next": "15.1.0", "next-themes": "^0.4.3", @@ -55,8 +55,8 @@ "eslint-config-next": "15.0.3", "fs": "^0.0.1-security", "next-auth": "^5.0.0-beta.25", - "postcss": "^8", - "tailwindcss": "^3.4.15", + "postcss": "^8.5.1", + "tailwindcss": "^3.4.17", "tsx": "^4.19.2", "typescript": "^5.6.3" } @@ -3596,6 +3596,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "browserslist": "^4.23.3", "caniuse-lite": "^1.0.30001646", @@ -5668,16 +5669,19 @@ } }, "node_modules/framer-motion": { - "version": "11.11.17", - "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.11.17.tgz", - "integrity": "sha512-O8QzvoKiuzI5HSAHbcYuL6xU+ZLXbrH7C8Akaato4JzQbX2ULNeniqC2Vo5eiCtFktX9XsJ+7nUhxcl2E2IjpA==", + "version": "11.18.0", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.18.0.tgz", + "integrity": "sha512-Vmjl5Al7XqKHzDFnVqzi1H9hzn5w4eN/bdqXTymVpU2UuMQuz9w6UPdsL9dFBeH7loBlnu4qcEXME+nvbkcIOw==", + "license": "MIT", "dependencies": { + "motion-dom": "^11.16.4", + "motion-utils": "^11.16.0", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", - "react": "^18.0.0", - "react-dom": "^18.0.0" + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@emotion/is-prop-valid": { @@ -6688,11 +6692,15 @@ } }, "node_modules/lilconfig": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" } }, "node_modules/lines-and-columns": { @@ -6795,6 +6803,21 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/motion-dom": { + "version": "11.16.4", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.16.4.tgz", + "integrity": "sha512-2wuCie206pCiP2K23uvwJeci4pMFfyQKpWI0Vy6HrCTDzDCer4TsYtT7IVnuGbDeoIV37UuZiUr6SZMHEc1Vww==", + "license": "MIT", + "dependencies": { + "motion-utils": "^11.16.0" + } + }, + "node_modules/motion-utils": { + "version": "11.16.0", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-11.16.0.tgz", + "integrity": "sha512-ngdWPjg31rD4WGXFi0eZ00DQQqKKu04QExyv/ymlC+3k+WIgYVFbt6gS5JsFPbJODTF/r8XiE/X+SsoT9c0ocw==", + "license": "MIT" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -6811,15 +6834,16 @@ } }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -7394,9 +7418,9 @@ } }, "node_modules/postcss": { - "version": "8.4.49", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", - "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz", + "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==", "funding": [ { "type": "opencollective", @@ -7411,8 +7435,9 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "nanoid": "^3.3.7", + "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -7488,17 +7513,6 @@ } } }, - "node_modules/postcss-load-config/node_modules/lilconfig": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", - "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, "node_modules/postcss-nested": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", @@ -8455,9 +8469,10 @@ } }, "node_modules/tailwindcss": { - "version": "3.4.15", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.15.tgz", - "integrity": "sha512-r4MeXnfBmSOuKUWmXe6h2CcyfzJCEk4F0pptO5jlnYSIViUkVmsawj80N5h2lO3gwcmSb4n3PuN+e+GC1Guylw==", + "version": "3.4.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", + "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", + "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -8468,7 +8483,7 @@ "glob-parent": "^6.0.2", "is-glob": "^4.0.3", "jiti": "^1.21.6", - "lilconfig": "^2.1.0", + "lilconfig": "^3.1.3", "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "object-hash": "^3.0.0", diff --git a/package.json b/package.json index dddc081..4ad8131 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "clsx": "^2.1.1", "dotenv": "^16.4.7", "fontsource-roboto": "^4.0.0", - "framer-motion": "^11.11.17", + "framer-motion": "^11.18.0", "lucide-react": "^0.460.0", "next": "15.1.0", "next-themes": "^0.4.3", @@ -56,8 +56,8 @@ "eslint-config-next": "15.0.3", "fs": "^0.0.1-security", "next-auth": "^5.0.0-beta.25", - "postcss": "^8", - "tailwindcss": "^3.4.15", + "postcss": "^8.5.1", + "tailwindcss": "^3.4.17", "tsx": "^4.19.2", "typescript": "^5.6.3" } diff --git a/src/app/Products/muzzle-devices/page.tsx b/src/app/Products/muzzle-devices/page.tsx index dede8a6..97d9518 100644 --- a/src/app/Products/muzzle-devices/page.tsx +++ b/src/app/Products/muzzle-devices/page.tsx @@ -1,4 +1,5 @@ import { getMuzzleDevices } from "@queries/PSA"; +import PageHero from "@components/PageHero"; import styles from '../styles.module.css'; import SortTable from "@components/SortTable"; diff --git a/src/app/Products/stocks/page.tsx b/src/app/Products/stocks/page.tsx index c203ade..24ad19d 100644 --- a/src/app/Products/stocks/page.tsx +++ b/src/app/Products/stocks/page.tsx @@ -1,9 +1,7 @@ import { getStocks } from "@queries/PSA"; -import { psa } from '@schemas/schema'; import partTypes from 'src/data/parts_cats.json'; import styles from '../styles.module.css'; import PageHero from "@components/PageHero"; -import ProductTable from "@components/ProductTable"; import SortTable from "@components/SortTable"; export default async function StocksPage() { diff --git a/src/app/UserRegister/page.tsx b/src/app/UserRegister/page.tsx new file mode 100644 index 0000000..6998282 --- /dev/null +++ b/src/app/UserRegister/page.tsx @@ -0,0 +1,4 @@ +import RegistrationForm from "@components/RegistrationForm"; +export default function RegisterPage() { + return ; +} diff --git a/src/app/api/auth/signin/route.tsx b/src/app/api/auth/signin/route.tsx index e644e32..5977bd8 100644 --- a/src/app/api/auth/signin/route.tsx +++ b/src/app/api/auth/signin/route.tsx @@ -1,6 +1,6 @@ import { NextResponse } from 'next/server'; -import { db } from '../../../../db'; -import { users } from '../../../../drizzle/schema'; +import { db } from '@db/index'; +import { users } from '@schemas/schema'; import bcrypt from 'bcryptjs'; import { eq } from 'drizzle-orm'; diff --git a/src/app/register/page.tsx b/src/app/register/page.tsx index 3b59f19..dfc07e6 100644 --- a/src/app/register/page.tsx +++ b/src/app/register/page.tsx @@ -1,7 +1,7 @@ 'use client'; import React, { useState } from 'react'; import { useRouter } from 'next/navigation'; -import PageHero from '../../components/PageHero'; +import PageHero from '@components/PageHero'; import Link from 'next/link'; export default function RegisterPage() { diff --git a/src/components/RegistrationForm/index.tsx b/src/components/RegistrationForm/index.tsx new file mode 100644 index 0000000..b8f12ae --- /dev/null +++ b/src/components/RegistrationForm/index.tsx @@ -0,0 +1,122 @@ +'use client' + +import { useState } from 'react' +import { useRouter } from 'next/navigation' + +export default function RegistrationForm() { + const router = useRouter() + const [error, setError] = useState('') + const [formData, setFormData] = useState({ + name: '', + email: '', + password: '', + confirmPassword: '' + }) + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault() + setError('') + + if (formData.password !== formData.confirmPassword) { + setError('Passwords do not match') + return + } + + try { + // Add your registration API call here + console.log('Form submitted:', formData) + router.push('/login') // Redirect to login after successful registration + } catch (err) { + setError('Failed to register user') + } + } + + return ( +
+
+
+

+ Create your account +

+
+
+
+
+ + setFormData({...formData, name: e.target.value})} + /> +
+
+ + setFormData({...formData, email: e.target.value})} + /> +
+
+ + setFormData({...formData, password: e.target.value})} + /> +
+
+ + setFormData({...formData, confirmPassword: e.target.value})} + /> +
+
+ + {error && ( +
{error}
+ )} + +
+ +
+
+
+
+ ) +} \ No newline at end of file diff --git a/src/components/UserRegistration/index.tsx b/src/components/UserRegistration/index.tsx deleted file mode 100644 index 7ad3f5a..0000000 --- a/src/components/UserRegistration/index.tsx +++ /dev/null @@ -1,99 +0,0 @@ -// components/UserRegistration.js -import { useState } from 'react'; -import { useToast } from '@chakra-ui/toast'; -import { FormControl, FormLabel, } from '@chakra-ui/form-control'; -import { Input, Button, Stack, Heading } from '@chakra-ui/react'; -import { drizzle } from 'drizzle-orm/node-postgres'; -import { users } from '@schemas/schema'; - - -export default function UserRegistration() { - const [formData, setFormData] = useState({ - username: '', - email: '', - password: '', - }); - - const toast = useToast(); - - const handleChange = (e) => { - const { name, value } = e.target; - setFormData({ ...formData, [name]: value }); - }; - - const handleSubmit = async (e) => { - e.preventDefault(); - - // Example of database interaction using drizzle-orm: - const db = drizzle(/* your database connection logic */); - - try { - // Replace 'users' with your actual table name - await db.insert(users).values(formData); - toast({ - title: 'Account created.', - description: "We've created your account successfully!", - status: 'success', - duration: 5000, - isClosable: true, - }); - - // Reset the form - setFormData({ username: '', email: '', password: '' }); - } catch (error) { - toast({ - title: 'An error occurred.', - description: error.message, - status: 'error', - duration: 5000, - isClosable: true, - }); - } - }; - - return ( - - User Registration -
- - Username - - - - - Email - - - - - Password - - - - -
-
- ); -} diff --git a/src/drizzle/schema/schema.ts b/src/drizzle/schema/schema.ts index 89a134c..a2422d2 100644 --- a/src/drizzle/schema/schema.ts +++ b/src/drizzle/schema/schema.ts @@ -23,6 +23,7 @@ export const users = pgTable("users", { name: text("name"), email: text("email").unique(), emailVerified: timestamp("emailVerified", { mode: "date" }), + passwordHash: varchar("password_hash", { length: 255 }).notNull(), image: text("image"), }) @@ -30,6 +31,7 @@ export const userskeep = pgTable("users-keep", { id: bigserial({ mode: "bigint" }).primaryKey().notNull(), username: varchar({ length: 50 }).notNull(), email: varchar({ length: 255 }).notNull(), + passwordHash: varchar("password_hash", { length: 255 }).notNull(), firstName: varchar("first_name", { length: 50 }), lastName: varchar("last_name", { length: 50 }), @@ -50,6 +52,33 @@ export const userskeep = pgTable("users-keep", { usersBuildPrivacySettingCheck: check("users_build_privacy_setting_check", sql`build_privacy_setting = ANY (ARRAY['private'::text, 'public'::text])`), } }); +export const usersMerged = pgTable("users-merged", { + id: text("id") + .primaryKey() + .$defaultFn(() => crypto.randomUUID()), + username: varchar({ length: 50 }).notNull(), + email: varchar({ length: 255 }).notNull(), + emailVerifiedOn: timestamp("emailVerified", { mode: "date" }), + passwordHash: varchar("password_hash", { length: 255 }).notNull(), + firstName: varchar("first_name", { length: 50 }), + lastName: varchar("last_name", { length: 50 }), + profilePicture: varchar("profile_picture", { length: 255 }), + dateOfBirth: date("date_of_birth"), + phoneNumber: varchar("phone_number", { length: 20 }), + createdAt: timestamp("created_at", { mode: 'string' }).default(sql`CURRENT_TIMESTAMP`), + updatedAt: timestamp("updated_at", { mode: 'string' }).default(sql`CURRENT_TIMESTAMP`), + isAdmin: boolean("is_admin").default(false), + lastLogin: timestamp("last_login", { mode: 'string' }), + emailVerified: boolean("email_verified").default(false), + buildPrivacySetting: text("build_privacy_setting").default('public'), + uuid: uuid().defaultRandom(), +}, (table) => { + return { + usersUsernameKey: unique("users-merged_username_key").on(table.username), + usersEmailKey: unique("users-merged_email_key").on(table.email), + usersBuildPrivacySettingCheck: check("users-merged_build_privacy_setting_check", sql`build_privacy_setting = ANY (ARRAY['private'::text, 'public'::text])`), + } +}); export const categories = pgTable("categories", { id: integer().primaryKey().generatedAlwaysAsIdentity({ name: "categories_id_seq", startWith: 1, increment: 1, minValue: 1, maxValue: 2147483647, cache: 1 }),