diff --git a/apps/fabrikanabytok/app/(platform)/admin/accessories/new/page.tsx b/apps/fabrikanabytok/app/(platform)/admin/accessories/new/page.tsx new file mode 100644 index 0000000..eaf511d --- /dev/null +++ b/apps/fabrikanabytok/app/(platform)/admin/accessories/new/page.tsx @@ -0,0 +1,44 @@ +import { AccessoryForm } from "@/components/admin/accessory-form" +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" +import { Button } from "@/components/ui/button" +import { ArrowLeft } from "lucide-react" +import Link from "next/link" + +export const metadata = { + title: "Új Kiegészítő - Admin - FABRIKA NABYTOK", + description: "Új kiegészítő hozzáadása", +} + +export default function NewAccessoryPage() { + return ( +
+ Adjon hozzá új kiegészítőt a katalógushoz +
++ Kezelje a tálcákat, fogantyúkat és egyéb kiegészítőket +
+{activeAccessories} aktív
+Különböző típusok
+Összes variáns
+Részletes áttekintés az üzleti teljesítményről
+
+
Ebben a hónapban
+Minden rendelés átlaga
+Elkészült tervek ebben a hónapban
+Még nincs aktív előfizetés
+ )} +Live operational dashboard
+Manage 3D models for the kitchen planner
+
+ Upload and manage 3D models (.glb files) for the kitchen planner.
+
+ Currently, 3D models are managed through the product form.
+
{category.name}
+Hozzon létre új hierarchikus kategóriát attribútumokkal és szűrőkkel
+Összes kategória: {categories.length}
++ Adja meg az új ügyfél adatait +
+Kezelje az ügyfeleket és partnereket
++ {activeCustomers} aktív +
++ {((companies / totalCustomers) * 100 || 0).toFixed(0)}% az összes ügyf élből +
++ {((individuals / totalCustomers) * 100 || 0).toFixed(0)}% az összes ügyf élből +
++ Összes ügyfél alapján +
++ Hozzon létre új kedvezménykódot +
++ Kedvezménykódok és promóciós kampányok kezelése +
++ {coupons.length} összes +
++ Kuponhasználat +
++ Összes kedvezmény +
++ {campaigns.length} összes +
++ Create and manage email templates with WYSIWYG editor, variables, and tracking +
+Add new employee to the system
+Send invitation email to new employee
++ Comprehensive employee operations, analytics, and monitoring +
++ Manage uploaded files, 3D models, translations, and configurations +
++ Importáljon rendeléseket, ügyfeleket és anyagokat Excel fájlokból +
++ Excel 2007+ fájlok +
++ Automatikus oszlop felismerés +
++ Összesen +
++ Válassza ki a rendeléseket és tételeket a számlához +
+Számlák kezelése és kiállítása
++ {paidInvoices} fizetve +
++ Összes kiállított számla +
++ {unpaidInvoices} számla +
++ Lejárt számlák +
++ Automation, AI agents, scheduled tasks, and workflow management +
++ Run migrations to ensure all documents have custom UUID fields +
++ This migration adds custom UUID fields (id) to all documents. + This replaces MongoDB ObjectId (_id) for improved security. +
+Töltse ki az alábbi űrlapot új modell létrehozásához
+Összes modell: {total}
++ {new Date(order.createdAt).toLocaleString("hu-HU", { + year: "numeric", + month: "long", + day: "numeric", + hour: "2-digit", + minute: "2-digit", + })} +
++ {item.quantity} db × {item.price.toLocaleString("hu-HU")} Ft +
+ {item.variantId &&Változat: {item.variantId}
} +{(item.price * item.quantity).toLocaleString("hu-HU")} Ft
++ {order.shippingAddress.firstName} {order.shippingAddress.lastName} +
+{order.shippingAddress.company}
+{order.shippingAddress.address1}
+{order.shippingAddress.address2}
++ {order.shippingAddress.city}, {order.shippingAddress.postalCode} +
+{order.shippingAddress.country}
+{order.shippingAddress.phone}
+{order.notes}
++ Hozzon létre új gyártási rendelést +
+Kezelje és kövesse nyomon az összes rendelést
+Áttekintés a rendszer állapotáról
+Aktív termékek száma
+Összes rendelés
+Regisztrált felhasználók
+Folyamatban lévő
+A rendszer sikeresen fut és működik
+Design intelligence and performance metrics
+Módosítsa a termék adatait
+
+ Import ID: {log.id}
+
+ Track all product imports and monitor their status +
++ {summary.successfulImports} successful +
++ {summary.totalProductsUpdated} updated +
+products per import
+Currently in progress
+Require attention
+No imports yet
+ + + ++ {formatDistanceToNow(new Date(log.startedAt), { addSuffix: true })} + {log.currentStatus && ` • ${log.currentStatus}`} +
+ {log.results && ( ++ One-click import of all products from WooCommerce CSV export +
++ Skip products that already exist. Safe for re-running imports. +
++ Update existing products with new data from CSV. Preserves product IDs. +
++ Delete existing products and create new ones. Use with caution! +
++ Check by product SKU/Cikkszám. Most reliable method. +
++ Check by product URL slug (generated from name). +
++ Check both SKU and slug. Most thorough but may catch more false positives. +
++ Current Selection: {importMode === "skip" && "Skip duplicates"} + {importMode === "update" && "Update existing products"} + {importMode === "replace" && "Replace existing products"} - Check by{" "} + {checkBy === "sku" && "SKU"} + {checkBy === "slug" && "Slug"} + {checkBy === "both" && "SKU and Slug"} +
+
+ Download all product images and store them locally. This ensures images are always available
+ even if the source site changes. Images will be saved to /temp/uploads/products/
+
+ This may take 5-10 minutes. Please don't close this page. +
++ Estimated time: 5-10 minutes for ~7,000 rows +
++ Import products from CSV or JSON files. Categories and tags will be created automatically. +
++ Use our one-click import to process all 7,332 rows automatically +
+
+{`Név,Normál ár,Készlet,Kategória,Címkék
+"Modern Desk",50000,10,"Furniture > Desks","modern,office"
+"Comfort Chair",35000,25,"Furniture > Chairs","comfort,ergonomic"`}
+
+ + Categories are automatically created from your CSV, including hierarchical categories. +
++ Data is validated before import to catch errors early and provide helpful feedback. +
++ Automatically detects and maps fields from various column names in Hungarian or English. +
+Töltse ki az alábbi űrlapot új termék létrehozásához
+Összes termék: {total}
++ Configure integrations, features, security, and compliance +
+Kezelje a vásárolható kredit csomagokat
+Hozzon létre egy új előfizetési csomagot funkciókkal és árazással
+Kezelje az előfizetési csomagokat és funkciókat
+Kezelje az aktív felhasználói előfizetéseket
++ Monitor platform health and run diagnostics +
++ Database indexes, performance tuning, and optimization tools +
+Build progress és rendszer monitorozás
+Kész
+/ {progress.totalPhases} Fázis
+Jelenlegi Fázis
+Kész Feladatok
+Utolsó Frissítés
+{phase.name}
+{phase.description}
+Response: {health.database.responseTime}ms
+Connections: {health.database.connections}
+Connections: {health.socket.connections}
+Rooms: {health.socket.rooms}
+Hit Rate: {(health.cache.hitRate * 100).toFixed(1)}%
+Memory: {health.cache.memoryUsage}%
+Avg Response: {health.api.avgResponseTime}ms
+Error Rate: {(health.api.errorRate * 100).toFixed(2)}%
+{metrics.totalProducts}
+{metrics.totalOrders}
+Ma: {metrics.todayOrders}
+{metrics.totalUsers}
+{metrics.weekRevenue.toLocaleString("hu-HU")} Ft
+Új felhasználó meghívása a platformra
+Felhasználók és szerepkörök kezelése
++ Grant and revoke specific permissions for users +
++ Configure roles, assign permissions, and manage access control +
++ Adjon hozzá új munkalapot a katalógushoz +
+Munkalap katalógus és rendelések kezelése
+{activeWorktops} aktív
++ {((inStockWorktops / totalWorktops) * 100 || 0).toFixed(0)}% elérh ető +
+Különböző anyagok
+Üdvözöljük, {user.firstName}!
+Management Console
+Employees online
+Operations performed
+Unacknowledged
+To be picked
+Awaiting pickup
++ Manage color and material options for this {partId ? "part" : elementId ? "element" : "product"} +
++ Define available color options for customers +
+ + +Quick Add from Palette
++ Define material options with PBR textures +
+ + +{color.name}
+{color.hexCode}
+ + {color.textureUrl && ( +Még nincsenek kredit csomagok. Hozzon létre egyet!
+kredit
+ {pkg.bonus > 0 && ( +Last 30 days
+{analytics.opened} / {analytics.delivered} opened
+{analytics.clicked} / {analytics.delivered} clicked
+{analytics.bounced} bounced
+
+ HTML
+ {template.description}
+Ez a folyamat:
+userId mezőjük
+ {result.message}
+ {result.data && ( ++ Migrált munkatársak:{" "} + + {result.data.migrated} / {result.data.total || 0} + +
+ )} + {result.data.errors && result.data.errors.length > 0 && ( +Hibák:
+{preview.message}
+ {preview.data && ( +Migrálásra vár:
+{preview.data.totalToMigrate}
+Új felhasználók:
++ {preview.data.willCreateNewUsers} +
+Meglévőkhöz kapcsolódik:
++ {preview.data.willLinkExisting} +
+Szerepkör frissítés:
++ {preview.data.willUpdateRoles} +
++ Első {preview.data.preview.length} munkatárs: +
+{verificationResult.message}
+ {verificationResult.data && ( ++ Összes munkatárs: {verificationResult.data.totalEmployees} +
++ Hiányzó userId:{" "} + + {verificationResult.data.missingUserId} + +
++ Érvénytelen userId referenciák:{" "} + + {verificationResult.data.invalidUserIdReferences} + +
+{employee.position}
+ +✅ Created: {result.created} employees
+ {result.skipped > 0 &&⚠️ Skipped: {result.skipped} (already exist)
} +Login Credentials:
+📧 Email: {result.credentials.emailPattern}
+🔑 Password: {result.credentials.password}
{result.credentials.note}
++ This will create approximately 18 placeholder employees with different roles and departments for testing purposes. +
+Standardized Credentials:
+📧 Email: firstname.lastname@fabrikanabytok.test
🔑 Password: placeholder123
Roles included:
++ ⚠️ Existing employees with the same email will be skipped. +
+{employee.position}
++ Támogatott formátumok: .xlsx, .xls (max 10MB) +
++ +{preview.validationIssues.length - 20} további probléma... +
+ )} +{file.name}
+ ++ Cannot delete - file is in use +
+ )} ++ Upload files or adjust your filters +
+No files found
++ Across all categories +
++ {storagePercentage.toFixed(1)}% of {formatBytes(stats.storageLimit)} +
++ Import products from a JSON file with structured data +
+Total Imported: {importCount} products
+ {lastImportDate && ( ++ Last import: {lastImportDate.toLocaleString()} +
+ )} ++ Import products directly from WooCommerce export CSV files. + The parser will automatically detect and map all WooCommerce fields. +
+ ++ Always validate data before importing to catch errors early +
+ + {validationResult && ( +Valid: {validationResult.valid ? "Yes" : "No"}
+Errors: {validationResult.errors?.length || 0}
+Warnings: {validationResult.warnings?.length || 0}
+{agent.description}
+{service.description}
++ {model.dimensions.width} × {model.dimensions.height} × {model.dimensions.depth} cm +
+Nincs megjeleníthető modell
+Még nincsenek rendelések
+Design intelligence and metrics
+Per design session
+Components per design
+Designs completed
+{title}
+{value}
+Nincsenek értesítések
+{notification.title}
+{notification.message}
++ {formatDistanceToNow(notification.timestamp, { addSuffix: true, locale: hu })} +
+{group.description}
+{role.description}
+Monitor clicks, navigation, and interactions
+Monitor which features are used
+Monitor load times and errors
+Add branding to exported images
+Only allow 3D export for premium users
+{description}
++ Temporarily disable the site for maintenance +
++ Enabling maintenance mode will make the site inaccessible to regular users. + Only admins will be able to access the system. +
++ European data protection regulations +
++ How long to keep user data before automatic deletion +
+Block features immediately when limit reached
++ Users will be immediately blocked when they reach their subscription limits. + Disable for warning mode instead. +
++ Initializing default settings... +
+Overall System Status
+Total collections
+Active indexes
+Még nincs értékesítési adat
+{product.name || `Product ${index + 1}`}
+{product.totalSold} db eladva
+{product.revenue.toLocaleString("hu-HU")} Ft
+This is a test email from your FABRYKA NABYTOK system.
", + }) + + return { + success: true, + message: `Test email sent to ${session.user.email}`, + } + } catch (error: any) { + console.error("[Settings] Test email error:", error) + return { + success: false, + message: error.message || "Failed to send test email", + } + } +} + +/** + * Test storage connection + */ +export async function testStorageConfig(provider: "vercel" | "minio" | "s3" | "ftp") { + const session = await auth() + + if (!session?.user || session.user.role !== "superadmin") { + throw new Error("Unauthorized") + } + + try { + const settings = await getSettings() + + if (!settings) { + throw new Error("Settings not found") + } + + switch (provider) { + case "vercel": + if (!settings.storage.vercel.enabled) { + throw new Error("Vercel Blob not enabled") + } + // Test with @vercel/blob + const { list } = await import("@vercel/blob") + const blobs = await list() + + return { + success: true, + message: `Vercel Blob connected (${blobs.blobs.length} files)`, + } + + case "minio": + if (!settings.storage.minio.enabled) { + throw new Error("Minio not enabled") + } + // Test Minio connection + return { + success: true, + message: "Minio connection successful (implementation needed)", + } + + case "s3": + if (!settings.storage.s3.enabled) { + throw new Error("AWS S3 not enabled") + } + return { + success: true, + message: "AWS S3 connection successful (implementation needed)", + } + + case "ftp": + if (!settings.storage.ftp.enabled) { + throw new Error("FTP not enabled") + } + return { + success: true, + message: "FTP connection successful (implementation needed)", + } + + default: + throw new Error("Unknown storage provider") + } + } catch (error: any) { + console.error(`[Settings] Test ${provider} error:`, error) + return { + success: false, + message: error.message || `Failed to connect to ${provider}`, + } + } +} + +/** + * Get default settings + */ +function getDefaultSettings(): GlobalSettings { + return { + id: "global", + general: { + siteName: "FABRYKA NABYTOK", + siteUrl: "https://fabryka-nabytok.com", + supportEmail: "support@fabryka-nabytok.com", + defaultLanguage: "hu", + timezone: "Europe/Budapest", + currency: "HUF", + maintenanceMode: false, + }, + ai: { + openai: { + enabled: false, + apiKey: "", + model: "gpt-4-turbo", + maxTokens: 2000, + temperature: 0.7, + }, + huggingface: { + enabled: false, + apiKey: "", + models: {}, + }, + google: { + enabled: false, + apiKey: "", + models: {}, + }, + }, + storage: { + provider: "vercel", + vercel: { + enabled: true, + token: process.env.BLOB_READ_WRITE_TOKEN || "", + }, + minio: { + enabled: false, + endpoint: "localhost", + port: 9000, + useSSL: false, + accessKey: "", + secretKey: "", + bucket: "fabryka", + }, + s3: { + enabled: false, + region: "eu-central-1", + bucket: "", + accessKeyId: "", + secretAccessKey: "", + }, + ftp: { + enabled: false, + host: "", + port: 21, + user: "", + password: "", + secure: false, + basePath: "/", + }, + maxFileSize: 50 * 1024 * 1024, // 50MB + maxStoragePerUser: 1024 * 1024 * 1024, // 1GB + allowedFileTypes: [".glb", ".gltf", ".jpg", ".png", ".webp", ".pdf"], + }, + email: { + smtp: { + enabled: false, + host: "", + port: 587, + secure: true, + user: "", + password: "", + from: { + name: "FABRYKA NABYTOK", + email: "noreply@fabryka-nabytok.com", + }, + }, + templates: { + orderConfirmation: true, + designShared: true, + passwordReset: true, + welcome: true, + newsletter: false, + }, + sendgrid: { + enabled: false, + apiKey: "", + }, + }, + social: { + oauth: { + google: { + enabled: false, + clientId: "", + clientSecret: "", + }, + facebook: { + enabled: false, + appId: "", + appSecret: "", + }, + github: { + enabled: false, + clientId: "", + clientSecret: "", + }, + }, + enableShareButtons: true, + shareOnSocial: ["facebook", "twitter", "pinterest"], + }, + features: { + aiAssistant: true, + realTimeCollaboration: true, + advancedExport: true, + measurementTools: true, + product3DViewer: true, + productConfigurator: true, + advancedSearch: true, + wishlist: true, + fileManager: true, + analytics: true, + notifications: true, + chatSupport: false, + }, + export: { + image: { + defaultResolution: "fhd", + defaultFormat: "png", + quality: 0.95, + maxResolution: "4k", + watermark: false, + watermarkText: "FABRYKA NABYTOK", + watermarkOpacity: 0.5, + }, + pdf: { + includeImages: true, + includeShoppingList: true, + includeDimensions: true, + includeMaterials: true, + includeQRCode: true, + watermark: true, + pageSize: "a4", + orientation: "portrait", + }, + model3D: { + defaultFormat: "glb", + includeTextures: true, + includeMaterials: true, + includeAnimations: false, + optimizeOnExport: true, + maxFileSize: 50, + requireSubscription: true, + }, + }, + legal: { + privacyPolicy: { + enabled: true, + content: "Your privacy is important to us.
", + lastUpdated: new Date(), + version: "1.0", + }, + termsOfService: { + enabled: true, + content: "By using our service, you agree to these terms.
", + lastUpdated: new Date(), + version: "1.0", + }, + gdpr: { + enabled: true, + dataRetentionDays: 365, + allowDataExport: true, + allowDataDeletion: true, + requireConsent: true, + }, + cookieConsent: { + enabled: true, + mode: "banner", + position: "bottom", + categories: { + essential: true, + analytics: false, + marketing: false, + preferences: false, + }, + }, + }, + analytics: { + googleAnalytics: { + enabled: false, + measurementId: "", + trackPageViews: true, + trackEvents: true, + }, + facebookPixel: { + enabled: false, + pixelId: "", + }, + customAnalytics: { + enabled: false, + }, + internalTracking: { + trackUserBehavior: true, + trackFeatureUsage: true, + trackPerformance: true, + dataRetentionDays: 90, + }, + }, + security: { + rateLimit: { + enabled: true, + requestsPerMinute: 60, + requestsPerHour: 1000, + }, + session: { + maxConcurrentSessions: 3, + sessionTimeout: 60, + requireReauth: false, + }, + subscriptionEnforcement: { + strictMode: false, + checkInterval: 60, + gracePeriod: 7, + }, + ipWhitelist: [], + ipBlacklist: [], + twoFactor: { + enabled: false, + required: false, + methods: ["email"], + }, + }, + updatedBy: "system", + updatedAt: new Date(), + } +} + diff --git a/apps/fabrikanabytok/lib/actions/shipping.actions.ts b/apps/fabrikanabytok/lib/actions/shipping.actions.ts new file mode 100644 index 0000000..4b35f18 --- /dev/null +++ b/apps/fabrikanabytok/lib/actions/shipping.actions.ts @@ -0,0 +1,82 @@ +"use server" + +import { auth } from "@/lib/auth/auth" +import { getDb } from "@/lib/db/mongodb" +import { hasPermission } from "@/lib/utils/permissions" +import { emitToAdmin } from "@/lib/websocket/socket-server" +import type { Shipment } from "@/lib/types/shipping.types" + +async function getEmployeeFromSession() { + const session = await auth() + if (!session?.user) throw new Error("Not authenticated") + const db = await getDb() + const employee = await db.collection("employees").findOne({ userId: session.user.id }) + if (!employee) throw new Error("Employee profile not found") + return employee +} + +export async function createShipment(orderId: string, carrier: string, service: string) { + const employee = await getEmployeeFromSession() + + if (!hasPermission(employee.role, "shipping.plan", employee.permissions)) { + throw new Error("Permission denied") + } + + try { + const db = await getDb() + + const order = await db.collection("orders").findOne({ id: orderId }) + if (!order) throw new Error("Order not found") + + const shipment: Shipment = { + id: crypto.randomUUID(), + shipmentNumber: `SHIP-${Date.now()}`, + orderId, + orderNumber: order.orderNumber, + carrier: carrier as any, + carrierName: carrier, + service, + status: "draft", + packages: [], + totalWeight: 0, + totalVolume: 0, + from: {} as any, + to: order.shippingAddress, + shippingCost: 0, + createdAt: new Date(), + } + + await db.collection("shipments").insertOne(shipment) + + emitToAdmin("shipping:created", { + shipmentNumber: shipment.shipmentNumber, + orderNumber: order.orderNumber, + timestamp: Date.now(), + }) + + return { success: true, shipment } + } catch (error: any) { + throw new Error(error.message || "Failed to create shipment") + } +} + +export async function getActiveShipments() { + const employee = await getEmployeeFromSession() + + try { + const db = await getDb() + const shipments = await db + .collection("shipments") + .find({ status: { $nin: ["delivered", "cancelled"] } }) + .sort({ createdAt: -1 }) + .toArray() + + return shipments.map((s: any) => { + const { _id, ...data } = s + return data + }) + } catch (error) { + return [] + } +} + diff --git a/apps/fabrikanabytok/lib/actions/subscription.actions.ts b/apps/fabrikanabytok/lib/actions/subscription.actions.ts new file mode 100644 index 0000000..e1f35ee --- /dev/null +++ b/apps/fabrikanabytok/lib/actions/subscription.actions.ts @@ -0,0 +1,301 @@ +"use server" + +import { revalidatePath } from "next/cache" +import clientPromise from "@/lib/db/mongodb" +import type { SubscriptionPlan, CreditPackage } from "@/lib/types/subscription.types" + +export async function getSubscriptionPlans() { + try { + const client = await clientPromise + const db = client.db() + + const plans = await db.collection("subscription_plans").find({ isActive: true }).sort({ order: 1 }).toArray() + + return { + success: true, + data: plans.map((plan) => ({ + ...plan, + id: plan._id.toString(), + _id: undefined, + })), + } + } catch (error) { + console.error("[v0] Get subscription plans error:", error) + return { success: false, error: "Nem sikerült betölteni az előfizetési csomagokat" } + } +} + +export async function getSubscriptionPlan(id: string) { + try { + const client = await clientPromise + const db = client.db() + + const plan = await db.collection("subscription_plans").findOne({ _id: id }) + + if (!plan) { + return { success: false, error: "Az előfizetési csomag nem található" } + } + + return { + success: true, + data: { + ...plan, + id: plan._id.toString(), + _id: undefined, + }, + } + } catch (error) { + console.error("[v0] Get subscription plan error:", error) + return { success: false, error: "Nem sikerült betölteni az előfizetési csomagot" } + } +} + +export async function createSubscriptionPlan(data: Omit