diff --git a/apps/fabrikanabytok/app/(auth)/login/page.tsx b/apps/fabrikanabytok/app/(auth)/login/page.tsx
new file mode 100644
index 0000000..d2cff8c
--- /dev/null
+++ b/apps/fabrikanabytok/app/(auth)/login/page.tsx
@@ -0,0 +1,38 @@
+import { LoginForm } from "@/components/auth/login-form"
+import Link from "next/link"
+
+export default function LoginPage() {
+ return (
+
+
+
+
+
FABRIKA NABYTOK
+
+
Bejelentkezés
+
+ Jelentkezzen be fiókjába a folytatáshoz
+
+
+ Ügyfelek, munkatársak és adminisztrátorok számára
+
+
+
+
+
+
+
+ Még nincs fiókja?{" "}
+
+ Regisztráljon itt
+
+
+
+
+
A bejelentkezés után automatikusan átirányítjuk Önt a megfelelő felületre.
+
+
+
+
+ )
+}
diff --git a/apps/fabrikanabytok/app/(auth)/register/page.tsx b/apps/fabrikanabytok/app/(auth)/register/page.tsx
new file mode 100644
index 0000000..d79622e
--- /dev/null
+++ b/apps/fabrikanabytok/app/(auth)/register/page.tsx
@@ -0,0 +1,27 @@
+import { RegisterForm } from "@/components/auth/register-form"
+import Link from "next/link"
+
+export default function RegisterPage() {
+ return (
+
+
+
+
+
FABRIKA NABYTOK
+
+
Regisztráció
+
Hozzon létre új fiókot a kezdéshez
+
+
+
+
+
+ Már van fiókja?{" "}
+
+ Jelentkezzen be itt
+
+
+
+
+ )
+}
diff --git a/apps/fabrikanabytok/app/(auth)/setup-wizard/page.tsx b/apps/fabrikanabytok/app/(auth)/setup-wizard/page.tsx
new file mode 100644
index 0000000..eae5010
--- /dev/null
+++ b/apps/fabrikanabytok/app/(auth)/setup-wizard/page.tsx
@@ -0,0 +1,31 @@
+import { isSystemInitialized } from "@/lib/db/setup"
+import { redirect } from "next/navigation"
+import { SetupWizardForm } from "@/components/auth/setup-wizard-form"
+
+export default async function SetupWizardPage() {
+ const initialized = await isSystemInitialized()
+
+ if (initialized) {
+ redirect("/")
+ }
+
+ return (
+
+
+
+
+
Üdvözöljük a FABRIKA NABYTOK rendszerben
+
+ A kezdéshez konfigurálja a rendszert és hozza létre a superadmin fiókot
+
+
+
+
+
+
+ )
+}
diff --git a/apps/fabrikanabytok/app/api/auth/[...nextauth]/route.ts b/apps/fabrikanabytok/app/api/auth/[...nextauth]/route.ts
new file mode 100644
index 0000000..c9ebf8e
--- /dev/null
+++ b/apps/fabrikanabytok/app/api/auth/[...nextauth]/route.ts
@@ -0,0 +1,3 @@
+import { handlers } from "@/lib/auth/auth"
+
+export const { GET, POST } = handlers
diff --git a/apps/fabrikanabytok/components/auth/login-form.tsx b/apps/fabrikanabytok/components/auth/login-form.tsx
new file mode 100644
index 0000000..f23eff4
--- /dev/null
+++ b/apps/fabrikanabytok/components/auth/login-form.tsx
@@ -0,0 +1,119 @@
+"use client"
+
+import type React from "react"
+
+import { useState } from "react"
+import { useRouter, useSearchParams } from "next/navigation"
+import { Button } from "@/components/ui/button"
+import { Input } from "@/components/ui/input"
+import { Label } from "@/components/ui/label"
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
+import { Loader2, AlertCircle, CheckCircle2 } from "lucide-react"
+import { loginAction } from "@/lib/actions/auth.actions"
+
+export function LoginForm() {
+ const router = useRouter()
+ const searchParams = useSearchParams()
+ const [loading, setLoading] = useState(false)
+ const [error, setError] = useState(null)
+ const setupSuccess = searchParams.get("setup") === "success"
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault()
+ setError(null)
+ setLoading(true)
+
+ const formData = new FormData(e.currentTarget)
+
+ try {
+ const result = await loginAction(formData)
+
+ if (!result.success) {
+ setError(result.message)
+ setLoading(false)
+ return
+ }
+
+ // Redirect based on callback URL or user role
+ const callbackUrl = searchParams.get("callbackUrl")
+
+ if (callbackUrl) {
+ router.push(callbackUrl)
+ } else if (result.redirectTo) {
+ // Use the role-based redirect from the server
+ router.push(result.redirectTo)
+ } else {
+ // Fallback to home
+ router.push("/")
+ }
+
+ router.refresh()
+ } catch (err) {
+ setError("Hiba történt a bejelentkezés során")
+ setLoading(false)
+ }
+ }
+
+ return (
+
+
+ Üdvözöljük vissza
+ Adja meg bejelentkezési adatait
+
+
+ {setupSuccess && (
+
+
+
Rendszer sikeresen inicializálva! Jelentkezzen be.
+
+ )}
+
+
+
+
+ )
+}
diff --git a/apps/fabrikanabytok/components/auth/register-form.tsx b/apps/fabrikanabytok/components/auth/register-form.tsx
new file mode 100644
index 0000000..2f06ce8
--- /dev/null
+++ b/apps/fabrikanabytok/components/auth/register-form.tsx
@@ -0,0 +1,149 @@
+"use client"
+
+import type React from "react"
+
+import { useState } from "react"
+import { useRouter } from "next/navigation"
+import { Button } from "@/components/ui/button"
+import { Input } from "@/components/ui/input"
+import { Label } from "@/components/ui/label"
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
+import { Loader2, AlertCircle } from "lucide-react"
+import { registerAction } from "@/lib/actions/auth.actions"
+
+export function RegisterForm() {
+ const router = useRouter()
+ const [loading, setLoading] = useState(false)
+ const [error, setError] = useState(null)
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault()
+ setError(null)
+ setLoading(true)
+
+ const formData = new FormData(e.currentTarget)
+ const password = formData.get("password") as string
+ const confirmPassword = formData.get("confirmPassword") as string
+
+ if (password !== confirmPassword) {
+ setError("A jelszavak nem egyeznek")
+ setLoading(false)
+ return
+ }
+
+ try {
+ const result = await registerAction(formData)
+
+ if (!result.success) {
+ setError(result.message)
+ return
+ }
+
+ // Redirect to profile after successful registration
+ router.push("/profile")
+ router.refresh()
+ } catch (err) {
+ setError("Hiba történt a regisztráció során")
+ } finally {
+ setLoading(false)
+ }
+ }
+
+ return (
+
+
+ Új fiók létrehozása
+ Töltse ki az alábbi mezőket a regisztrációhoz
+
+
+
+
+
+ )
+}
diff --git a/apps/fabrikanabytok/components/auth/setup-wizard-form.tsx b/apps/fabrikanabytok/components/auth/setup-wizard-form.tsx
new file mode 100644
index 0000000..2ea514e
--- /dev/null
+++ b/apps/fabrikanabytok/components/auth/setup-wizard-form.tsx
@@ -0,0 +1,245 @@
+"use client"
+
+import type React from "react"
+
+import { useState } from "react"
+import { useRouter } from "next/navigation"
+import { Button } from "@/components/ui/button"
+import { Input } from "@/components/ui/input"
+import { Label } from "@/components/ui/label"
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
+import { Loader2, CheckCircle2, AlertCircle } from "lucide-react"
+
+export function SetupWizardForm() {
+ const router = useRouter()
+ const [loading, setLoading] = useState(false)
+ const [error, setError] = useState(null)
+ const [step, setStep] = useState(1)
+ const [formData, setFormData] = useState({
+ email: "",
+ password: "",
+ confirmPassword: "",
+ firstName: "",
+ lastName: "",
+ companyName: "",
+ mongoUri: "",
+ })
+
+ const handleInputChange = (e: React.ChangeEvent) => {
+ setFormData((prev) => ({
+ ...prev,
+ [e.target.name]: e.target.value,
+ }))
+ setError(null)
+ }
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault()
+ setError(null)
+
+ // Validation
+ if (formData.password !== formData.confirmPassword) {
+ setError("A jelszavak nem egyeznek")
+ return
+ }
+
+ if (formData.password.length < 8) {
+ setError("A jelszónak legalább 8 karakter hosszúnak kell lennie")
+ return
+ }
+
+ setLoading(true)
+
+ try {
+ const response = await fetch("/api/setup/initialize", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({
+ email: formData.email,
+ password: formData.password,
+ firstName: formData.firstName,
+ lastName: formData.lastName,
+ companyName: formData.companyName || undefined,
+ }),
+ })
+
+ const data = await response.json()
+
+ if (!response.ok) {
+ throw new Error(data.message || "Hiba történt az inicializálás során")
+ }
+
+ // Success - redirect to login
+ router.push("/login?setup=success")
+ } catch (err) {
+ setError((err as Error).message)
+ } finally {
+ setLoading(false)
+ }
+ }
+
+ return (
+
+
+
+
+ {[1, 2].map((s) => (
+
+ ))}
+
+
{step}/2 lépés
+
+
+ {step === 1 ? "Superadmin fiók létrehozása" : "Vállalati információk"}
+
+
+ {step === 1
+ ? "Hozza létre a fő adminisztrátori fiókot a rendszer kezeléséhez"
+ : "Adja meg a vállalat adatait (opcionális)"}
+
+
+
+
+
+
+ )
+}
diff --git a/apps/fabrikanabytok/lib/auth/AUTHENTICATION_FLOW.md b/apps/fabrikanabytok/lib/auth/AUTHENTICATION_FLOW.md
new file mode 100644
index 0000000..220ec80
--- /dev/null
+++ b/apps/fabrikanabytok/lib/auth/AUTHENTICATION_FLOW.md
@@ -0,0 +1,285 @@
+# Authentication Flow Diagram
+
+## Login Flow
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│ User Login │
+│ (All user types use │
+│ same /login page) │
+└────────────────────────┬────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────────┐
+│ Submit Email + Password │
+└────────────────────────┬────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────────┐
+│ Step 1: Check Users Collection │
+│ db.collection("users").findOne({email}) │
+└────────────────────────┬────────────────────────────────────────┘
+ │
+ ├─────► User not found ──► Error
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────────┐
+│ Step 2: Validate Password │
+│ bcrypt.compare(password, user.password) │
+└────────────────────────┬────────────────────────────────────────┘
+ │
+ ├─────► Invalid ──► Error
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────────┐
+│ Step 3: Check Employee Collection │
+│ db.collection("employees").findOne({userId}) │
+└────────────────────────┬────────────────────────────────────────┘
+ │
+ ┌──────────────┴──────────────┐
+ │ │
+ ▼ ▼
+ Employee Found Employee NOT Found
+ │ │
+ ▼ ▼
+┌──────────────────────┐ ┌──────────────────────┐
+│ Update employee │ │ Regular user │
+│ lastLogin │ │ (customer, etc.) │
+└──────────┬───────────┘ └──────────┬───────────┘
+ │ │
+ └──────────────┬──────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────────┐
+│ Step 4: Construct Authenticated User │
+│ - Basic user data (id, email, name, role) │
+│ - Employee data if applicable: │
+│ * isEmployee: true │
+│ * employeeId, employeeNumber │
+│ * department, position │
+│ * permissions │
+└────────────────────────┬────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────────┐
+│ Step 5: Update Last Login │
+│ db.collection("users").updateOne( │
+│ {_id}, {$set: {lastLoginAt: new Date()}} │
+│ ) │
+└────────────────────────┬────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────────┐
+│ Step 6: Create JWT Session │
+│ - Include all user + employee data │
+│ - 7 day expiry │
+└────────────────────────┬────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────────┐
+│ Step 7: Determine Redirect │
+│ getDefaultRedirectForRole(role, isEmployee) │
+└────────────────────────┬────────────────────────────────────────┘
+ │
+ ┌──────────────┼──────────────┬──────────────┐
+ │ │ │ │
+ ▼ ▼ ▼ ▼
+ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
+ │ Employee │ │ Admin │ │Distribut.│ │ Customer │
+ │ /internal│ │ /admin │ │/distribu.│ │ /profile │
+ │/dashboard│ │/dashboard│ │/dashboard│ │ │
+ └──────────┘ └──────────┘ └──────────┘ └──────────┘
+```
+
+## Database Structure
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│ USERS Collection │
+│ (All authenticated users including employees) │
+├─────────────────────────────────────────────────────────────────┤
+│ { │
+│ _id: ObjectId │
+│ email: "user@example.com" │
+│ password: "hashed_bcrypt" │
+│ firstName: "John" │
+│ lastName: "Doe" │
+│ role: "warehouse_manager" | "customer" | "admin" | ... │
+│ emailVerified: true │
+│ lastLoginAt: Date │
+│ createdAt: Date │
+│ updatedAt: Date │
+│ } │
+└─────────────────────────────────────────────────────────────────┘
+ │
+ │ Linked by userId
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────────┐
+│ EMPLOYEES Collection │
+│ (Additional employee-specific data) │
+├─────────────────────────────────────────────────────────────────┤
+│ { │
+│ _id: ObjectId │
+│ userId: "user_id_reference" ◄─── References users._id │
+│ employeeNumber: "EMP-001" │
+│ role: "warehouse_manager" │
+│ department: "Logistics" │
+│ position: "Manager" │
+│ permissions: ["inventory.view", "orders.pick", ...] │
+│ status: "active" | "inactive" | "suspended" │
+│ hireDate: Date │
+│ lastLogin: Date │
+│ metrics: {...} │
+│ schedule: {...} │
+│ createdAt: Date │
+│ updatedAt: Date │
+│ } │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+## Session Structure
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│ SESSION │
+├─────────────────────────────────────────────────────────────────┤
+│ For Regular User (Customer): │
+│ { │
+│ user: { │
+│ id: "user_123" │
+│ email: "customer@example.com" │
+│ firstName: "Jane" │
+│ lastName: "Smith" │
+│ role: "customer" │
+│ avatar: "https://..." │
+│ emailVerified: true │
+│ isEmployee: false │
+│ } │
+│ } │
+├─────────────────────────────────────────────────────────────────┤
+│ For Employee: │
+│ { │
+│ user: { │
+│ id: "user_456" │
+│ email: "employee@example.com" │
+│ firstName: "John" │
+│ lastName: "Doe" │
+│ role: "warehouse_manager" │
+│ avatar: "https://..." │
+│ emailVerified: true │
+│ isEmployee: true │
+│ employeeId: "emp_789" │
+│ employeeNumber: "EMP-001" │
+│ department: "Logistics" │
+│ position: "Manager" │
+│ permissions: ["inventory.view", "orders.pick", ...] │
+│ } │
+│ } │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+## Role-Based Access Control
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│ Protected Routes │
+└─────────────────────────────────────────────────────────────────┘
+ │
+ ┌──────────────┼──────────────┬──────────────┐
+ │ │ │ │
+ ▼ ▼ ▼ ▼
+
+/internal/* /admin/* /distributor/* /profile
+ │ │ │ │
+ ▼ ▼ ▼ ▼
+┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
+│Check: │ │Check: │ │Check: │ │Check: │
+│isEmployee│ │isAdmin │ │role == │ │Any auth │
+│== true │ │Role │ │distribut.│ │user │
+└──────────┘ └──────────┘ └──────────┘ └──────────┘
+ │ │ │ │
+ ▼ ▼ ▼ ▼
+ Warehouse Dashboard Distributor Personal
+ Inventory Analytics Portal Profile
+ Orders Users Orders Settings
+ Shipping Products Catalogs History
+ Reports Settings Commission Wishlist
+```
+
+## Permission Hierarchy
+
+```
+ ┌───────────────┐
+ │ SUPERADMIN │
+ │ (All access) │
+ └───────┬───────┘
+ │
+ ┌───────▼───────┐
+ │ ADMIN │
+ │ (Admin panel)│
+ └───────┬───────┘
+ │
+ ┌─────────────────┼─────────────────┐
+ │ │ │
+ ▼ ▼ ▼
+ ┌────────────┐ ┌────────────┐ ┌────────────┐
+ │ DISTRIBUTOR│ │ EMPLOYEE │ │ CUSTOMER │
+ │ (Business) │ │ (Internal) │ │ (Public) │
+ └────────────┘ └─────┬──────┘ └────────────┘
+ │
+ ┌─────────────────┼─────────────────┐
+ │ │ │
+ ▼ ▼ ▼
+ ┌──────────┐ ┌──────────┐ ┌──────────┐
+ │Warehouse │ │Inventory │ │ Picker │
+ │ Manager │ │ Clerk │ │ Packer │
+ └──────────┘ └──────────┘ └──────────┘
+ (Full access) (Limited) (Task-based)
+```
+
+## Migration Flow
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│ OLD STRUCTURE │
+│ Employee has separate login credentials │
+├─────────────────────────────────────────────────────────────────┤
+│ employees collection: │
+│ { │
+│ email: "employee@example.com" │
+│ password: "hashed_password" │
+│ firstName: "John" │
+│ lastName: "Doe" │
+│ ... other employee data ... │
+│ } │
+└─────────────────────────────────────────────────────────────────┘
+ │
+ │ MIGRATION SCRIPT
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────────┐
+│ NEW STRUCTURE │
+├─────────────────────────────────────────────────────────────────┤
+│ Step 1: Create user in users collection │
+│ { │
+│ email: "employee@example.com" │
+│ password: "hashed_password" (copied from employee) │
+│ firstName: "John" │
+│ lastName: "Doe" │
+│ role: "warehouse_manager" │
+│ } │
+│ │ │
+│ │ userId = user._id │
+│ │ │
+│ Step 2: Update employee record │
+│ { │
+│ userId: "user_id_reference" ◄─── NEW │
+│ employeeNumber: "EMP-001" │
+│ department: "Logistics" │
+│ ... other employee data ... │
+│ (password field removed) │
+│ } │
+└─────────────────────────────────────────────────────────────────┘
+```
+
diff --git a/apps/fabrikanabytok/lib/auth/MIGRATION_GUIDE.md b/apps/fabrikanabytok/lib/auth/MIGRATION_GUIDE.md
new file mode 100644
index 0000000..1b5dce6
--- /dev/null
+++ b/apps/fabrikanabytok/lib/auth/MIGRATION_GUIDE.md
@@ -0,0 +1,314 @@
+# Employee Migration Guide
+
+## Overview
+
+This guide explains how to use the Employee Migration Panel to migrate existing employees from the old authentication system to the new unified authentication system.
+
+## Access
+
+The migration panel is **only accessible to superadmin users** and can be found at:
+
+**Path:** `/admin/users`
+
+The panel appears at the top of the Users page as an orange-highlighted card.
+
+## Migration Process
+
+### Step 1: Preview the Migration
+
+Before running the migration, **always preview** what will happen:
+
+1. Click the **"Előnézet" (Preview)** button
+2. Review the information displayed:
+ - **Total employees to migrate**: Number of employees without `userId`
+ - **New users to create**: Employees without existing user accounts
+ - **Link to existing**: Employees with existing user accounts
+ - **Role updates**: Existing users whose roles will be updated
+ - **Employee list**: Preview of first 10 employees
+
+#### What the Preview Shows
+
+```
+Migráció előnézet
+━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+Found X employees to migrate
+
+┌─────────────────────┬──────────────────────┐
+│ Migrálásra vár: 15 │ Új felhasználók: 12 │
+├─────────────────────┼──────────────────────┤
+│ Meglévőkhöz: 3 │ Szerepkör frissítés: 2│
+└─────────────────────┴──────────────────────┘
+
+Első 10 munkatárs:
+• John Doe (john@example.com) [Új]
+• Jane Smith (jane@example.com) [Meglévő] customer → warehouse_manager
+• ...
+```
+
+### Step 2: Run the Migration
+
+Once you've reviewed the preview and are ready to proceed:
+
+1. Click **"Migráció futtatása" (Run Migration)**
+2. A confirmation dialog appears
+3. Read the warning carefully
+4. Click **"Migráció indítása" (Start Migration)**
+5. Wait for the process to complete
+
+The migration will:
+- ✅ Create user accounts for employees without existing accounts
+- ✅ Link employees to existing user accounts (preserves existing users)
+- ✅ Update roles if needed (employee role takes precedence)
+- ✅ Remove duplicate passwords from employees collection
+- ✅ Update timestamps
+
+### Step 3: Automatic Verification
+
+After successful migration, the system automatically runs a verification check.
+
+The verification displays:
+- **Total employees**: Total count in employees collection
+- **Missing userId**: Should be 0 after successful migration
+- **Invalid userId references**: Should be 0 after successful migration
+
+### Step 4: Manual Verification (Optional)
+
+You can manually verify the migration at any time:
+
+1. Click **"Ellenőrzés" (Verify)**
+2. Review the results
+
+## Understanding the Results
+
+### Successful Migration
+
+```
+✅ Sikeres migráció
+
+Successfully migrated 15 employees
+
+Migrált munkatársak: 15 / 15
+
+━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+✅ Ellenőrzés eredménye
+
+Migration verified successfully
+
+Összes munkatárs: 15
+Hiányzó userId: 0 ✓
+Érvénytelen userId referenciák: 0 ✓
+```
+
+### Migration with Errors
+
+```
+⚠️ Sikeres migráció
+
+Successfully migrated 13 employees
+
+Migrált munkatársak: 13 / 15
+
+Hibák:
+• Failed to migrate employee@example.com: Email already exists
+• Failed to migrate test@example.com: Invalid email format
+```
+
+If you see errors, check the employee records manually and fix any data issues.
+
+## What Gets Migrated
+
+### Before Migration
+
+**Employees Collection:**
+```json
+{
+ "_id": "emp123",
+ "email": "john@example.com",
+ "password": "hashed_password", // ← Stored here
+ "firstName": "John",
+ "lastName": "Doe",
+ "role": "warehouse_manager",
+ "department": "Logistics",
+ // ... other fields
+}
+```
+
+**Users Collection:**
+```json
+// No user exists yet for this employee
+```
+
+### After Migration
+
+**Employees Collection:**
+```json
+{
+ "_id": "emp123",
+ "userId": "user456", // ← NEW: Links to user
+ "email": "john@example.com",
+ // password field removed
+ "firstName": "John",
+ "lastName": "Doe",
+ "role": "warehouse_manager",
+ "department": "Logistics",
+ // ... other fields
+}
+```
+
+**Users Collection:**
+```json
+{
+ "_id": "user456", // ← NEW: User created
+ "email": "john@example.com",
+ "password": "hashed_password", // ← Moved here
+ "firstName": "John",
+ "lastName": "Doe",
+ "role": "warehouse_manager",
+ "emailVerified": true,
+ "createdAt": "2025-11-26T...",
+ "updatedAt": "2025-11-26T..."
+}
+```
+
+## Preserving Existing Users
+
+### Scenario: Employee with Existing User Account
+
+If an employee's email matches an existing user:
+
+**Before:**
+- Users: `{email: "john@example.com", role: "customer"}`
+- Employees: `{email: "john@example.com", role: "warehouse_manager"}`
+
+**After:**
+- Users: `{email: "john@example.com", role: "warehouse_manager"}` ← Role updated
+- Employees: `{userId: "user123", email: "john@example.com"}` ← Linked
+
+The existing user account is **preserved** and only the role is updated to match the employee role.
+
+## Safety Features
+
+### 1. Preview Before Migration
+Always shows what will happen before making changes.
+
+### 2. Existing User Protection
+- Preserves existing user accounts
+- Only updates roles when necessary
+- Never deletes data
+
+### 3. Verification
+Automatic and manual verification ensures data integrity.
+
+### 4. Rollback Capability
+For testing purposes, you can rollback the migration.
+
+⚠️ **WARNING**: Rollback should only be used in development/testing!
+
+## Rollback (Testing Only)
+
+If you need to test the migration multiple times:
+
+1. Click **"Visszaállítás" (Rollback)**
+2. Confirm the action
+3. This removes the `userId` field from all employees
+4. You can now run the migration again
+
+⚠️ **IMPORTANT**:
+- Rollback does NOT delete created user accounts
+- It only removes the `userId` link
+- Use with caution in production
+
+## Troubleshooting
+
+### Issue: Preview shows 0 employees
+
+**Cause**: All employees already have `userId` set.
+
+**Solution**: Migration already completed or not needed.
+
+### Issue: Migration fails for some employees
+
+**Possible causes**:
+- Invalid email format
+- Missing required fields
+- Database connection issues
+
+**Solution**:
+1. Check error messages
+2. Fix data issues in the employees collection
+3. Run migration again (already migrated employees are skipped)
+
+### Issue: Verification shows missing userId
+
+**Cause**: Migration was interrupted or failed.
+
+**Solution**: Run the migration again. It will only process employees without `userId`.
+
+### Issue: Invalid userId references
+
+**Cause**: User was deleted but employee still references it.
+
+**Solution**:
+1. Find the affected employee(s)
+2. Either create the missing user or update the employee's userId
+
+## Post-Migration Checklist
+
+After successful migration:
+
+- [ ] Verification shows 0 missing userId
+- [ ] Verification shows 0 invalid references
+- [ ] Test employee login on `/login`
+- [ ] Verify session contains employee data
+- [ ] Check role-based redirects work correctly
+- [ ] Test employee-specific features
+- [ ] Review migration logs for any errors
+
+## Security Notes
+
+1. **Superadmin Only**: Only superadmin users can access the migration panel
+2. **Password Security**: Existing hashed passwords are moved, not re-hashed
+3. **Session Integrity**: Existing sessions are not affected
+4. **Database Backups**: Always backup before running in production
+5. **Audit Trail**: Migration actions are logged
+
+## Production Checklist
+
+Before running migration in production:
+
+1. [ ] **Backup database** completely
+2. [ ] Run preview and review results
+3. [ ] Test migration in staging environment first
+4. [ ] Schedule during low-traffic period
+5. [ ] Notify team members
+6. [ ] Run migration
+7. [ ] Verify results immediately
+8. [ ] Test employee logins
+9. [ ] Monitor for issues
+10. [ ] Document completion
+
+## Support
+
+If you encounter issues:
+
+1. Check the error messages in the migration panel
+2. Review the employee and user collections manually
+3. Check the server logs for detailed error information
+4. Contact technical support with:
+ - Screenshot of error
+ - Number of employees to migrate
+ - Any error messages from verification
+
+## Migration Statistics
+
+The migration panel tracks:
+- Total employees processed
+- Successfully migrated
+- Errors encountered
+- New users created
+- Existing users linked
+- Roles updated
+
+Use this information to ensure complete and accurate migration.
+
diff --git a/apps/fabrikanabytok/lib/auth/README.md b/apps/fabrikanabytok/lib/auth/README.md
new file mode 100644
index 0000000..89d63f5
--- /dev/null
+++ b/apps/fabrikanabytok/lib/auth/README.md
@@ -0,0 +1,263 @@
+# Unified Authentication System
+
+## Overview
+
+The authentication system has been refactored to support a unified login experience for all user types:
+- **Visitors** (unauthenticated users)
+- **Customers** (registered users)
+- **Distributors** (business partners)
+- **Employees** (internal staff with various roles)
+- **Administrators** (admin and superadmin)
+
+## Architecture
+
+### Database Structure
+
+#### Users Collection
+All authenticated users (including employees) are stored in the `users` collection:
+
+```typescript
+{
+ _id: ObjectId,
+ email: string,
+ password: string (hashed),
+ firstName: string,
+ lastName: string,
+ role: UserRole,
+ avatar?: string,
+ emailVerified: boolean,
+ lastLoginAt?: Date,
+ createdAt: Date,
+ updatedAt: Date,
+ // ... other user fields
+}
+```
+
+#### Employees Collection
+Employee-specific data is stored separately and linked via `userId`:
+
+```typescript
+{
+ _id: ObjectId,
+ userId: string, // Reference to users._id
+ employeeNumber: string,
+ role: EmployeeRole,
+ department: string,
+ position: string,
+ permissions: Permission[],
+ status: "active" | "inactive" | "suspended",
+ lastLogin?: Date,
+ // ... other employee fields
+}
+```
+
+### Authentication Flow
+
+1. **User Login**
+ - User submits email and password
+ - System checks `users` collection for authentication
+ - Password is validated using bcrypt
+
+2. **Employee Check**
+ - After successful authentication, system checks `employees` collection
+ - Searches for employee record with matching `userId`
+ - If found, employee data is merged into the session
+
+3. **Session Creation**
+ - JWT token is created with user data
+ - Employee-specific fields are included if applicable:
+ - `isEmployee`: boolean
+ - `employeeId`: string
+ - `employeeNumber`: string
+ - `department`: string
+ - `position`: string
+ - `permissions`: Permission[]
+
+4. **Role-Based Redirect**
+ - System determines appropriate redirect based on:
+ - User role
+ - Employee status
+ - Callback URL (if provided)
+
+### Redirect Logic
+
+The `getDefaultRedirectForRole()` function determines where users are sent after login:
+
+| User Type | Role | Redirect Path |
+|-----------|------|---------------|
+| Employee | warehouse_manager, inventory_clerk, etc. | `/internal/dashboard` |
+| Admin | admin, superadmin | `/admin/dashboard` |
+| Distributor | distributor | `/distributor/dashboard` |
+| Customer | customer | `/profile` |
+| Visitor | visitor | `/` |
+
+## Key Files
+
+### Auth Configuration
+- **`lib/auth/auth.ts`** - NextAuth configuration and credentials provider
+- **`lib/auth/auth.config.ts`** - Base auth configuration
+- **`lib/auth/auth-helpers.ts`** - Role-based utilities and redirect logic
+
+### Types
+- **`lib/types/user.types.ts`** - User and role type definitions
+- **`lib/types/employee.types.ts`** - Employee-specific types
+- **`lib/types/next-auth.d.ts`** - NextAuth type extensions
+
+### Actions
+- **`lib/actions/auth.actions.ts`** - Server actions for login/logout/register
+
+### Components
+- **`components/auth/login-form.tsx`** - Login form component
+- **`app/(auth)/login/page.tsx`** - Login page
+
+## Usage Examples
+
+### Creating an Employee
+
+To create a new employee:
+
+1. First, create a user account in the `users` collection:
+```typescript
+const user = {
+ email: "employee@example.com",
+ password: await hash("password", 12),
+ firstName: "John",
+ lastName: "Doe",
+ role: "warehouse_manager", // Employee role
+ emailVerified: true,
+ createdAt: new Date(),
+ updatedAt: new Date(),
+}
+const result = await db.collection("users").insertOne(user)
+```
+
+2. Then, create the employee record linked to the user:
+```typescript
+const employee = {
+ userId: result.insertedId.toString(),
+ employeeNumber: "EMP-001",
+ role: "warehouse_manager",
+ department: "Logistics",
+ position: "Manager",
+ permissions: [...], // Role-based permissions
+ status: "active",
+ hireDate: new Date(),
+ createdAt: new Date(),
+ updatedAt: new Date(),
+}
+await db.collection("employees").insertOne(employee)
+```
+
+### Accessing Session Data
+
+In server components:
+```typescript
+import { auth } from "@/lib/auth/auth"
+
+const session = await auth()
+
+if (session?.user) {
+ const { role, isEmployee, employeeId, permissions } = session.user
+
+ if (isEmployee) {
+ // Employee-specific logic
+ }
+}
+```
+
+In client components:
+```typescript
+import { useSession } from "next-auth/react"
+
+const { data: session } = useSession()
+
+if (session?.user.isEmployee) {
+ // Employee-specific UI
+}
+```
+
+### Checking Permissions
+
+```typescript
+import { canAccessAdmin, canAccessInternal } from "@/lib/auth/auth-helpers"
+
+const session = await auth()
+
+if (canAccessAdmin(session.user.role)) {
+ // Admin panel access
+}
+
+if (canAccessInternal(session.user.role, session.user.isEmployee)) {
+ // Internal employee portal access
+}
+```
+
+## Security Considerations
+
+1. **Password Storage**: All passwords are hashed using bcrypt with 12 rounds
+2. **Session Management**: JWT-based sessions with 7-day expiry
+3. **Role Validation**: Role and permission checks are performed server-side
+4. **Database Queries**: Protected against injection via MongoDB driver
+5. **Employee Verification**: Two-step verification (user + employee record)
+
+## Migration Guide
+
+### Migrating Existing Employees
+
+If you have employees with separate login credentials:
+
+1. **Backup existing data**
+2. **For each employee:**
+ ```typescript
+ // Create user account
+ const user = await db.collection("users").insertOne({
+ email: employee.email,
+ password: employee.password, // Already hashed
+ firstName: employee.firstName,
+ lastName: employee.lastName,
+ role: employee.role,
+ emailVerified: true,
+ createdAt: employee.createdAt,
+ updatedAt: new Date(),
+ })
+
+ // Update employee record
+ await db.collection("employees").updateOne(
+ { _id: employee._id },
+ {
+ $set: {
+ userId: user.insertedId.toString(),
+ updatedAt: new Date()
+ }
+ }
+ )
+ ```
+
+## Troubleshooting
+
+### Issue: "Hibás email vagy jelszó"
+- Verify user exists in `users` collection
+- Check password hash matches
+- Ensure user is not suspended/inactive
+
+### Issue: Employee not recognized
+- Verify `employees` collection has record with matching `userId`
+- Check that `userId` matches the `_id` from users collection
+- Ensure employee status is "active"
+
+### Issue: Wrong redirect after login
+- Check user role in database
+- Verify `isEmployee` flag in session
+- Review `getDefaultRedirectForRole()` logic
+
+## Future Enhancements
+
+- [ ] Two-factor authentication (2FA)
+- [ ] OAuth providers (Google, Microsoft)
+- [ ] Remember me functionality
+- [ ] Login attempt rate limiting
+- [ ] Account lockout after failed attempts
+- [ ] Password reset flow
+- [ ] Email verification flow
+- [ ] Session management dashboard
+
diff --git a/apps/fabrikanabytok/lib/auth/auth-helpers.ts b/apps/fabrikanabytok/lib/auth/auth-helpers.ts
new file mode 100644
index 0000000..71af949
--- /dev/null
+++ b/apps/fabrikanabytok/lib/auth/auth-helpers.ts
@@ -0,0 +1,135 @@
+/**
+ * Authentication Helper Utilities
+ * Role-based redirects and user type detection
+ */
+
+import type { UserRole } from "@/lib/types/user.types"
+
+/**
+ * Get default redirect path based on user role
+ */
+export function getDefaultRedirectForRole(role: UserRole, isEmployee?: boolean): string {
+ // Employee roles
+ if (isEmployee) {
+ switch (role) {
+ case "warehouse_manager":
+ case "inventory_clerk":
+ case "picker":
+ case "packer":
+ case "shipper":
+ case "location_manager":
+ case "outsource_coordinator":
+ case "quality_controller":
+ case "assistant":
+ return "/internal/dashboard"
+ default:
+ break
+ }
+ }
+
+ // Admin and superadmin
+ if (role === "admin" || role === "superadmin") {
+ return "/admin/dashboard"
+ }
+
+ // Distributor
+ if (role === "distributor") {
+ return "/distributor/dashboard"
+ }
+
+ // Customer
+ if (role === "customer") {
+ return "/profile"
+ }
+
+ // Visitor (default)
+ return "/"
+}
+
+/**
+ * Check if user role is an employee role
+ */
+export function isEmployeeRole(role: UserRole): boolean {
+ const employeeRoles: UserRole[] = [
+ "warehouse_manager",
+ "inventory_clerk",
+ "picker",
+ "packer",
+ "shipper",
+ "location_manager",
+ "outsource_coordinator",
+ "quality_controller",
+ "assistant",
+ ]
+ return employeeRoles.includes(role)
+}
+
+/**
+ * Check if user role is an admin role
+ */
+export function isAdminRole(role: UserRole): boolean {
+ return role === "admin" || role === "superadmin"
+}
+
+/**
+ * Check if user has access to admin panel
+ */
+export function canAccessAdmin(role: UserRole): boolean {
+ return isAdminRole(role)
+}
+
+/**
+ * Check if user has access to internal employee portal
+ */
+export function canAccessInternal(role: UserRole, isEmployee?: boolean): boolean {
+ return isEmployee === true || isEmployeeRole(role)
+}
+
+/**
+ * Get user type label for display
+ */
+export function getUserTypeLabel(role: UserRole, isEmployee?: boolean): string {
+ if (isEmployee || isEmployeeRole(role)) {
+ return "Munkatárs"
+ }
+
+ switch (role) {
+ case "superadmin":
+ return "Szuper adminisztrátor"
+ case "admin":
+ return "Adminisztrátor"
+ case "distributor":
+ return "Viszonteladó"
+ case "customer":
+ return "Ügyfél"
+ case "visitor":
+ return "Látogató"
+ default:
+ return "Felhasználó"
+ }
+}
+
+/**
+ * Get role display name in Hungarian
+ */
+export function getRoleDisplayName(role: UserRole): string {
+ const roleNames: Record = {
+ visitor: "Látogató",
+ customer: "Ügyfél",
+ distributor: "Viszonteladó",
+ admin: "Adminisztrátor",
+ superadmin: "Szuper adminisztrátor",
+ warehouse_manager: "Raktárvezető",
+ inventory_clerk: "Készletkezelő",
+ picker: "Komissiózó",
+ packer: "Csomagoló",
+ shipper: "Szállítmányozó",
+ location_manager: "Helyszín menedzser",
+ outsource_coordinator: "Beszerzési koordinátor",
+ quality_controller: "Minőségellenőr",
+ assistant: "Asszisztens",
+ }
+
+ return roleNames[role] || role
+}
+
diff --git a/apps/fabrikanabytok/lib/auth/auth.config.ts b/apps/fabrikanabytok/lib/auth/auth.config.ts
new file mode 100644
index 0000000..aa49fcc
--- /dev/null
+++ b/apps/fabrikanabytok/lib/auth/auth.config.ts
@@ -0,0 +1,8 @@
+import type { NextAuthConfig } from "next-auth"
+import Credentials from "next-auth/providers/credentials"
+
+export default {
+ providers: [
+ Credentials
+ ]
+} satisfies NextAuthConfig
\ No newline at end of file
diff --git a/apps/fabrikanabytok/lib/auth/auth.ts b/apps/fabrikanabytok/lib/auth/auth.ts
new file mode 100644
index 0000000..811beb2
--- /dev/null
+++ b/apps/fabrikanabytok/lib/auth/auth.ts
@@ -0,0 +1,162 @@
+import NextAuth from "next-auth"
+import authConfig from "./auth.config"
+import { MongoClient } from "mongodb"
+import { MongoDBAdapter } from "@auth/mongodb-adapter"
+import Credentials from "next-auth/providers/credentials"
+import { getDb, getClient } from "@/lib/db/mongodb"
+import bcrypt from "bcryptjs"
+import type { UserRole } from "@/lib/types/user.types"
+import { ObjectId } from "mongodb"
+import { Permission } from "../types/employee.types"
+
+const client = await getClient()
+const db = await getDb()
+
+export const { handlers, auth, signIn, signOut } = NextAuth({
+ ...authConfig,
+ adapter: MongoDBAdapter(client as unknown as MongoClient) as any,
+ session: {
+ strategy: "jwt",
+ maxAge: 7 * 24 * 60 * 60, // 7 days
+ },
+ pages: {
+ signIn: "/login",
+ error: "/login",
+ },
+ providers: [
+ Credentials({
+ id: "credentials",
+ name: "credentials",
+ credentials: {
+ email: { label: "Email", type: "email" },
+ password: { label: "Password", type: "password" },
+ },
+ // @ts-ignore
+ async authorize(credentials) {
+
+ if (!db) {
+ throw new Error("Database not connected")
+ }
+
+ if (!credentials?.email || !credentials?.password) {
+ throw new Error("Hibás email vagy jelszó")
+ }
+
+ // Step 1: Authenticate against users collection
+ const user = await db
+ .collection("users")
+ .findOne({ email: credentials.email })
+
+ if (!user) {
+ throw new Error("Hibás email vagy jelszó")
+ }
+
+ // Step 2: Verify password
+ const isValidPassword = await bcrypt.compare(
+ credentials.password as string,
+ user.password
+ )
+
+ if (!isValidPassword) {
+ throw new Error("Hibás email vagy jelszó")
+ }
+
+ // Step 3: Check if user is an employee
+ const employee = await db
+ .collection("employees")
+ .findOne({ userId: user._id.toString() })
+
+ // Step 4: Update last login timestamp
+ await db.collection("users").updateOne(
+ { _id: new ObjectId(user._id) },
+ {
+ $set: {
+ lastLoginAt: new Date(),
+ updatedAt: new Date()
+ }
+ }
+ )
+
+ // Step 5: If employee exists, update their last login
+ if (employee) {
+ await db.collection("employees").updateOne(
+ { _id: new ObjectId(employee._id) },
+ {
+ $set: {
+ lastLogin: new Date(),
+ updatedAt: new Date()
+ }
+ }
+ )
+ }
+
+ // Step 6: Construct the authenticated user object
+ const authenticatedUser = {
+ id: user._id.toString(),
+ email: user.email,
+ firstName: user.firstName,
+ lastName: user.lastName,
+ name: `${user.firstName || ''} ${user.lastName || ''}`.trim() || user.email,
+ role: employee ? employee.role : user.role,
+ avatar: user.avatar,
+ emailVerified: user.emailVerified,
+ // Include employee-specific data if applicable
+ employeeId: employee?._id?.toString(),
+ employeeNumber: employee?.employeeNumber,
+ department: employee?.department,
+ position: employee?.position,
+ permissions: employee?.permissions || [],
+ isEmployee: !!employee,
+ }
+
+ return authenticatedUser
+ }
+ })
+ ],
+ callbacks: {
+ async jwt({ token, user }) {
+ if (user) {
+ token.id = user.id
+ token.role = user.role
+ token.firstName = user.firstName
+ token.lastName = user.lastName
+ token.avatar = user.avatar
+ token.emailVerified = user.emailVerified as boolean | Date | undefined
+ // Employee-specific fields
+ token.isEmployee = user.isEmployee
+ token.employeeId = user.employeeId
+ token.employeeNumber = user.employeeNumber
+ token.department = user.department
+ token.position = user.position
+ token.permissions = user.permissions
+ }
+ return token
+ },
+ async session({ session, token }) {
+ if (session.user) {
+ session.user.id = token.id as string
+ session.user.role = token.role as UserRole
+ session.user.firstName = token.firstName as string
+ session.user.lastName = token.lastName as string
+ session.user.avatar = token.avatar as string
+ session.user.emailVerified = token.emailVerified as Date & boolean
+ // Employee-specific fields
+ session.user.isEmployee = token.isEmployee as boolean
+ session.user.employeeId = token.employeeId as string
+ session.user.employeeNumber = token.employeeNumber as string
+ session.user.department = token.department as string
+ session.user.position = token.position as string
+ session.user.permissions = token.permissions as Permission[]
+ }
+ return session
+ },
+ async redirect({ url, baseUrl }) {
+ // Allows relative callback URLs
+ if (url.startsWith("/")) return `${baseUrl}${url}`
+ // Allows callback URLs on the same origin
+ else if (new URL(url).origin === baseUrl) return url
+ return baseUrl
+ },
+ },
+ trustHost: true,
+})
diff --git a/apps/fabrikanabytok/lib/auth/migrate-employees.ts b/apps/fabrikanabytok/lib/auth/migrate-employees.ts
new file mode 100644
index 0000000..7c14ca7
--- /dev/null
+++ b/apps/fabrikanabytok/lib/auth/migrate-employees.ts
@@ -0,0 +1,259 @@
+/**
+ * Employee Migration Script
+ * Migrates employees from separate authentication to unified user system
+ */
+
+import { getDb } from "@/lib/db/mongodb"
+import { ObjectId } from "mongodb"
+
+interface OldEmployee {
+ _id: ObjectId
+ email: string
+ password: string // Already hashed
+ firstName: string
+ lastName: string
+ role: string
+ department: string
+ position: string
+ permissions: string[]
+ status: string
+ hireDate: Date
+ createdAt: Date
+ [key: string]: any
+}
+
+/**
+ * Migrate employees to the new unified authentication system
+ *
+ * This script:
+ * 1. Finds all employees without a userId
+ * 2. Creates corresponding user accounts
+ * 3. Links employees to users via userId
+ */
+export async function migrateEmployeesToUnifiedAuth() {
+ const db = await getDb()
+
+ if (!db) {
+ throw new Error("Database not connected")
+ }
+
+ console.log("🔄 Starting employee migration...")
+
+ try {
+ // Find employees without userId (old structure)
+ const employeesWithoutUser = await db
+ .collection("employees")
+ .find({
+ $or: [
+ { userId: { $exists: false } },
+ { userId: null },
+ { userId: "" }
+ ]
+ })
+ .toArray() as unknown as OldEmployee[]
+
+ console.log(`📊 Found ${employeesWithoutUser.length} employees to migrate`)
+
+ if (employeesWithoutUser.length === 0) {
+ console.log("✅ No employees to migrate")
+ return {
+ success: true,
+ migrated: 0,
+ errors: [],
+ }
+ }
+
+ const errors: string[] = []
+ let migratedCount = 0
+
+ for (const employee of employeesWithoutUser) {
+ try {
+ console.log(`\n👤 Processing: ${employee.firstName} ${employee.lastName} (${employee.email})`)
+
+ // Check if user already exists with this email
+ const existingUser = await db
+ .collection("users")
+ .findOne({ email: employee.email })
+
+ let userId: string
+
+ if (existingUser) {
+ console.log(` ℹ️ User already exists (${existingUser.role}), linking to employee...`)
+ userId = existingUser._id.toString()
+
+ // If the existing user has a different role, update it to the employee role
+ // This ensures employees have the correct role in the users collection
+ if (existingUser.role !== employee.role) {
+ console.log(` 🔄 Updating user role from ${existingUser.role} to ${employee.role}`)
+ await db.collection("users").updateOne(
+ { _id: existingUser._id },
+ {
+ $set: {
+ role: employee.role,
+ updatedAt: new Date()
+ }
+ }
+ )
+ }
+ } else {
+ // Create new user account
+ console.log(` ➕ Creating new user account...`)
+
+ const newUser = {
+ email: employee.email,
+ password: employee.password, // Use existing hashed password
+ firstName: employee.firstName,
+ lastName: employee.lastName,
+ role: employee.role,
+ avatar: employee.avatar || null,
+ phone: employee.phone || null,
+ emailVerified: true, // Employees are pre-verified
+ isActive: employee.status === "active",
+ creditBalance: 0,
+ createdAt: employee.createdAt || new Date(),
+ updatedAt: new Date(),
+ lastLoginAt: employee.lastLogin || null,
+ }
+
+ const userResult = await db.collection("users").insertOne(newUser)
+ userId = userResult.insertedId.toString()
+ console.log(` ✅ User created with ID: ${userId}`)
+ }
+
+ // Update employee record with userId
+ await db.collection("employees").updateOne(
+ { _id: employee._id },
+ {
+ $set: {
+ userId: userId,
+ updatedAt: new Date()
+ },
+ // Remove password from employee record (now stored in users)
+ $unset: { password: "" }
+ }
+ )
+
+ console.log(` ✅ Employee linked to user`)
+ migratedCount++
+
+ } catch (error) {
+ const errorMsg = `Failed to migrate ${employee.email}: ${error}`
+ console.error(` ❌ ${errorMsg}`)
+ errors.push(errorMsg)
+ }
+ }
+
+ console.log(`\n${"=".repeat(50)}`)
+ console.log(`✅ Migration complete!`)
+ console.log(` Migrated: ${migratedCount}/${employeesWithoutUser.length}`)
+
+ if (errors.length > 0) {
+ console.log(` Errors: ${errors.length}`)
+ console.log(`\n❌ Errors:`)
+ errors.forEach(err => console.log(` - ${err}`))
+ }
+
+ return {
+ success: true,
+ migrated: migratedCount,
+ total: employeesWithoutUser.length,
+ errors,
+ }
+
+ } catch (error) {
+ console.error("❌ Migration failed:", error)
+ throw error
+ }
+}
+
+/**
+ * Verify migration was successful
+ * Checks that all employees have valid userId references
+ */
+export async function verifyEmployeeMigration() {
+ const db = await getDb()
+
+ if (!db) {
+ throw new Error("Database not connected")
+ }
+
+ console.log("🔍 Verifying employee migration...")
+
+ // Check for employees without userId
+ const employeesWithoutUser = await db
+ .collection("employees")
+ .countDocuments({
+ $or: [
+ { userId: { $exists: false } },
+ { userId: null },
+ { userId: "" }
+ ]
+ })
+
+ // Check for employees with invalid userId
+ const employees = await db.collection("employees").find({}).toArray()
+ let invalidUserIds = 0
+
+ for (const employee of employees) {
+ if (employee.userId) {
+ const user = await db
+ .collection("users")
+ .findOne({ _id: new ObjectId(employee.userId) })
+
+ if (!user) {
+ console.log(`❌ Employee ${employee.email} has invalid userId: ${employee.userId}`)
+ invalidUserIds++
+ }
+ }
+ }
+
+ console.log(`\n📊 Verification Results:`)
+ console.log(` Total employees: ${employees.length}`)
+ console.log(` Without userId: ${employeesWithoutUser}`)
+ console.log(` Invalid userId references: ${invalidUserIds}`)
+
+ const isValid = employeesWithoutUser === 0 && invalidUserIds === 0
+
+ if (isValid) {
+ console.log(`\n✅ Migration verified successfully!`)
+ } else {
+ console.log(`\n❌ Migration has issues that need to be resolved`)
+ }
+
+ return {
+ success: isValid,
+ totalEmployees: employees.length,
+ missingUserId: employeesWithoutUser,
+ invalidUserIdReferences: invalidUserIds,
+ }
+}
+
+/**
+ * Rollback migration (for testing purposes)
+ * WARNING: This will remove the userId field from employees
+ */
+export async function rollbackEmployeeMigration() {
+ const db = await getDb()
+
+ if (!db) {
+ throw new Error("Database not connected")
+ }
+
+ console.log("⚠️ WARNING: Rolling back employee migration...")
+ console.log("This will remove userId from all employee records")
+
+ const result = await db.collection("employees").updateMany(
+ {},
+ {
+ $unset: { userId: "" }
+ }
+ )
+
+ console.log(`✅ Rolled back ${result.modifiedCount} employee records`)
+
+ return {
+ success: true,
+ modified: result.modifiedCount,
+ }
+}
+