diff --git a/backend/src/config.js b/backend/src/config.js index 3fb91a4..c4f0052 100644 --- a/backend/src/config.js +++ b/backend/src/config.js @@ -39,7 +39,7 @@ const config = { }, uploadDir: os.tmpdir(), email: { - from: 'Multi-Client Detergents POS ', + from: 'نظام إدارة المحل ونقطة البيع ', host: 'email-smtp.us-east-1.amazonaws.com', port: 587, auth: { diff --git a/backend/src/index.js b/backend/src/index.js index 9f7628a..6c1d505 100644 --- a/backend/src/index.js +++ b/backend/src/index.js @@ -55,8 +55,8 @@ const options = { openapi: "3.0.0", info: { version: "1.0.0", - title: "Multi-Client Detergents POS", - description: "Multi-Client Detergents POS Online REST API for Testing and Prototyping application. You can perform all major operations with your entities - create, delete and etc.", + title: "نظام إدارة المحل ونقطة البيع", + description: "واجهة API خاصة بإدارة المحل ونقطة البيع، وتوفّر عمليات الإدارة الأساسية على الكيانات والبيانات.", }, servers: [ { diff --git a/backend/src/middlewares/check-permissions.js b/backend/src/middlewares/check-permissions.js index 77740c7..998dfe0 100644 --- a/backend/src/middlewares/check-permissions.js +++ b/backend/src/middlewares/check-permissions.js @@ -7,30 +7,23 @@ let publicRoleCache = null; // Function to asynchronously fetch and cache the 'Public' role async function fetchAndCachePublicRole() { - try { - // Use RolesDBApi to find the role by name 'Public' - publicRoleCache = await RolesDBApi.findBy({ name: 'Public' }); + try { + // Use RolesDBApi to find the role by name 'Public' + publicRoleCache = await RolesDBApi.findBy({ name: 'Public' }); - if (!publicRoleCache) { - console.error("WARNING: Role 'Public' not found in database during middleware startup. Check your migrations."); - // The system might not function correctly without this role. May need to throw an error or use a fallback stub. - } else { - console.log("'Public' role successfully loaded and cached."); - } - } catch (error) { - console.error("Error fetching 'Public' role during middleware startup:", error); - // Handle the error during startup fetch - throw error; // Important to know if the app can proceed without the Public role + if (!publicRoleCache) { + console.error("WARNING: لم يتم العثور على دور 'Public' في قاعدة البيانات أثناء تهيئة الصلاحيات."); + } else { + console.log("تم تحميل دور 'Public' وحفظه في الذاكرة المؤقتة بنجاح."); } + } catch (error) { + console.error("حدث خطأ أثناء تحميل دور 'Public' عند بدء تشغيل وسيط الصلاحيات:", error); + throw error; + } } -// Trigger the role fetching when the check-permissions.js module is imported/loaded -// This should happen during application startup when routes are being configured. -fetchAndCachePublicRole().catch(error => { - // Handle the case where the fetchAndCachePublicRole promise is rejected - console.error("Critical error during permissions middleware initialization:", error); - // Decide here if the process should exit if the Public role is essential. - // process.exit(1); +fetchAndCachePublicRole().catch((error) => { + console.error('خطأ حرج أثناء تهيئة وسيط الصلاحيات:', error); }); /** @@ -39,85 +32,63 @@ fetchAndCachePublicRole().catch(error => { * @return {import("express").RequestHandler} Express middleware function. */ function checkPermissions(permission) { - return async (req, res, next) => { - const { currentUser } = req; + return async (req, res, next) => { + const { currentUser } = req; - // 1. Check self-access bypass (only if the user is authenticated) - if (currentUser && (currentUser.id === req.params.id || currentUser.id === req.body.id)) { - return next(); // User has access to their own resource + if (currentUser && (currentUser.id === req.params.id || currentUser.id === req.body.id)) { + return next(); + } + + if (currentUser) { + const customPermissions = Array.isArray(currentUser.custom_permissions) + ? currentUser.custom_permissions + : []; + const userPermission = customPermissions.find( + (cp) => cp.name === permission, + ); + if (userPermission) { + return next(); + } + } + + let effectiveRole = null; + try { + if (currentUser && currentUser.app_role) { + effectiveRole = currentUser.app_role; + } else if (!publicRoleCache) { + console.error("ذاكرة دور 'Public' فارغة. سيتم إعادة تحميله مباشرةً من قاعدة البيانات."); + effectiveRole = await RolesDBApi.findBy({ name: 'Public' }); + if (!effectiveRole) { + return next(new Error('خطأ داخلي: تعذر تحميل دور الوصول العام.')); } + } else { + effectiveRole = publicRoleCache; + } - // 2. Check Custom Permissions (only if the user is authenticated) - if (currentUser) { - // Ensure custom_permissions is an array before using find - const customPermissions = Array.isArray(currentUser.custom_permissions) - ? currentUser.custom_permissions - : []; - const userPermission = customPermissions.find( - (cp) => cp.name === permission, - ); - if (userPermission) { - return next(); // User has a custom permission - } - } + if (!effectiveRole) { + return next(new Error('خطأ داخلي: تعذر تحديد الدور الفعّال للتحقق من الصلاحية.')); + } - // 3. Determine the "effective" role for permission check - let effectiveRole = null; - try { - if (currentUser && currentUser.app_role) { - // User is authenticated and has an assigned role - effectiveRole = currentUser.app_role; - } else { - // User is NOT authenticated OR is authenticated but has no role - // Use the cached 'Public' role - if (!publicRoleCache) { - // If the cache is unexpectedly empty (e.g., startup error caught), - // we can try fetching the role again synchronously (less ideal) or just deny access. - console.error("Public role cache is empty. Attempting synchronous fetch..."); - // Less efficient fallback option: - effectiveRole = await RolesDBApi.findBy({ name: 'Public' }); // Could be slow - if (!effectiveRole) { - // If even the synchronous attempt failed - return next(new Error("Internal Server Error: Public role missing and cannot be fetched.")); - } - } else { - effectiveRole = publicRoleCache; // Use the cached object - } - } + let rolePermissions = []; + if (typeof effectiveRole.getPermissions === 'function') { + rolePermissions = await effectiveRole.getPermissions(); + } else if (Array.isArray(effectiveRole.permissions)) { + rolePermissions = effectiveRole.permissions; + } else { + console.error('تنسيق كائن الدور غير صالح ولا يحتوي على الصلاحيات المطلوبة:', effectiveRole); + return next(new Error('خطأ داخلي: بيانات الدور غير صالحة للتحقق من الصلاحيات.')); + } - // Check if we got a valid role object - if (!effectiveRole) { - return next(new Error("Internal Server Error: Could not determine effective role.")); - } - - // 4. Check Permissions on the "effective" role - // Assume the effectiveRole object (from app_role or RolesDBApi) has a getPermissions() method - // or a 'permissions' property (if permissions are eagerly loaded). - let rolePermissions = []; - if (typeof effectiveRole.getPermissions === 'function') { - rolePermissions = await effectiveRole.getPermissions(); // Get permissions asynchronously if the method exists - } else if (Array.isArray(effectiveRole.permissions)) { - rolePermissions = effectiveRole.permissions; // Or take from property if permissions are pre-loaded - } else { - console.error("Role object lacks getPermissions() method or permissions property:", effectiveRole); - return next(new Error("Internal Server Error: Invalid role object format.")); - } - - - if (rolePermissions.find((p) => p.name === permission)) { - next(); // The "effective" role has the required permission - } else { - // The "effective" role does not have the required permission - const roleName = effectiveRole.name || 'unknown role'; - next(new ValidationError('auth.forbidden', `Role '${roleName}' denied access to '${permission}'.`)); - } - - } catch (e) { - // Handle errors during role or permission fetching - console.error("Error during permission check:", e); - next(e); // Pass the error to the next middleware - } - }; + if (rolePermissions.find((p) => p.name === permission)) { + next(); + } else { + next(new ValidationError('auth.forbidden', `Role '${effectiveRole.name || 'unknown'}' denied '${permission}'.`)); + } + } catch (error) { + console.error('حدث خطأ أثناء التحقق من الصلاحيات:', error); + next(error); + } + }; } const METHOD_MAP = { @@ -134,16 +105,13 @@ const METHOD_MAP = { * @return {import("express").RequestHandler} Express middleware function. */ function checkCrudPermissions(name) { - return (req, res, next) => { - // Dynamically determine the permission name (e.g., 'READ_USERS') - const permissionName = `${METHOD_MAP[req.method]}_${name.toUpperCase()}`; - // Call the checkPermissions middleware with the determined permission - checkPermissions(permissionName)(req, res, next); - }; + return (req, res, next) => { + const permissionName = `${METHOD_MAP[req.method]}_${name.toUpperCase()}`; + checkPermissions(permissionName)(req, res, next); + }; } module.exports = { - checkPermissions, - checkCrudPermissions, + checkPermissions, + checkCrudPermissions, }; - diff --git a/backend/src/routes/openai.js b/backend/src/routes/openai.js index 2d47d9f..c563603 100644 --- a/backend/src/routes/openai.js +++ b/backend/src/routes/openai.js @@ -13,8 +13,8 @@ const loadRolesModules = () => { RolesDBApi: require('../db/api/roles'), }; } catch (error) { - console.error('Roles modules are missing. Advanced roles are required for this endpoint.', error); - const err = new Error('Roles modules are missing. Advanced roles are required for this endpoint.'); + console.error('تعذر تحميل وحدات الأدوار المطلوبة لهذا المسار.', error); + const err = new Error('تعذر تحميل وحدات الأدوار المطلوبة لهذا المسار.'); err.originalError = error; throw err; } @@ -310,7 +310,7 @@ router.post( if (!prompt) { return res.status(400).send({ success: false, - error: 'Prompt is required', + error: 'النص المطلوب للذكاء الاصطناعي فارغ', }); } diff --git a/backend/src/routes/pexels.js b/backend/src/routes/pexels.js index 8298595..be0e48f 100644 --- a/backend/src/routes/pexels.js +++ b/backend/src/routes/pexels.js @@ -19,7 +19,7 @@ router.get('/image', async (req, res) => { const data = await response.json(); res.status(200).json(data.photos[0]); } catch (error) { - res.status(200).json({ error: 'Failed to fetch image' }); + res.status(200).json({ error: 'تعذر جلب الصورة' }); } }); @@ -37,7 +37,7 @@ router.get('/video', async (req, res) => { const data = await response.json(); res.status(200).json(data.videos[0]); } catch (error) { - res.status(200).json({ error: 'Failed to fetch video' }); + res.status(200).json({ error: 'تعذر جلب الفيديو' }); } }); diff --git a/backend/src/routes/search.js b/backend/src/routes/search.js index 25da9e0..4a4afe5 100644 --- a/backend/src/routes/search.js +++ b/backend/src/routes/search.js @@ -41,15 +41,15 @@ router.post('/', async (req, res) => { const globalAccess = req.currentUser.app_role.globalAccess; if (!searchQuery) { - return res.status(400).json({ error: 'Please enter a search query' }); + return res.status(400).json({ error: 'يرجى إدخال عبارة للبحث' }); } try { const foundMatches = await SearchService.search(searchQuery, req.currentUser , organizationId, globalAccess,); res.json(foundMatches); } catch (error) { - console.error('Internal Server Error', error); - res.status(500).json({ error: 'Internal Server Error' }); + console.error('حدث خطأ داخلي في البحث', error); + res.status(500).json({ error: 'حدث خطأ داخلي أثناء تنفيذ البحث' }); } }); diff --git a/backend/src/routes/sql.js b/backend/src/routes/sql.js index b844f07..3daea1f 100644 --- a/backend/src/routes/sql.js +++ b/backend/src/routes/sql.js @@ -38,16 +38,16 @@ router.post( wrapAsync(async (req, res) => { const { sql } = req.body; if (typeof sql !== 'string' || !sql.trim()) { - return res.status(400).json({ error: 'SQL is required' }); + return res.status(400).json({ error: 'يرجى إدخال استعلام SQL' }); } const normalized = sql.trim().replace(/;+\s*$/, ''); if (!/^select\b/i.test(normalized)) { - return res.status(400).json({ error: 'Only SELECT statements are allowed' }); + return res.status(400).json({ error: 'يسمح فقط باستعلامات SELECT' }); } if (normalized.includes(';')) { - return res.status(400).json({ error: 'Only a single SELECT statement is allowed' }); + return res.status(400).json({ error: 'يسمح باستعلام SELECT واحد فقط في كل طلب' }); } const rows = await db.sequelize.query(normalized, { diff --git a/backend/src/services/file.js b/backend/src/services/file.js index 597be30..35a2b39 100644 --- a/backend/src/services/file.js +++ b/backend/src/services/file.js @@ -2,7 +2,7 @@ const formidable = require('formidable'); const fs = require('fs'); const config = require('../config'); const path = require('path'); -const { format } = require("util"); +const { format } = require('util'); const ensureDirectoryExistence = (filePath) => { const dirname = path.dirname(filePath); @@ -13,7 +13,7 @@ const ensureDirectoryExistence = (filePath) => { ensureDirectoryExistence(dirname); fs.mkdirSync(dirname); -} +}; const uploadLocal = ( folder, @@ -29,9 +29,7 @@ const uploadLocal = ( return; } - if ( - validations.entity - ) { + if (validations.entity) { res.sendStatus(403); return; } @@ -63,7 +61,7 @@ const uploadLocal = ( if (!filename) { fs.unlinkSync(fileTempUrl); - res.sendStatus(500); + res.status(500).send({ message: 'تعذر تحديد اسم الملف المطلوب رفعه' }); return; } @@ -80,59 +78,57 @@ const uploadLocal = ( form.on('error', function (err) { res.status(500).send(err); }); - } -} + }; +}; const downloadLocal = async (req, res) => { - const privateUrl = req.query.privateUrl; - if (!privateUrl) { - return res.sendStatus(404); - } - res.download(path.join(config.uploadDir, privateUrl)); -} + const privateUrl = req.query.privateUrl; + if (!privateUrl) { + return res.sendStatus(404); + } + res.download(path.join(config.uploadDir, privateUrl)); +}; const initGCloud = () => { - const processFile = require("../middlewares/upload"); - const { Storage } = require("@google-cloud/storage"); + const processFile = require('../middlewares/upload'); + const { Storage } = require('@google-cloud/storage'); - const crypto = require('crypto') - const hash = config.gcloud.hash - - const privateKey = process.env.GC_PRIVATE_KEY.replace(/\\\n/g, "\n"); + const hash = config.gcloud.hash; + const privateKey = process.env.GC_PRIVATE_KEY.replace(/\\\n/g, '\n'); const storage = new Storage({ - projectId: process.env.GC_PROJECT_ID, - credentials: { - client_email: process.env.GC_CLIENT_EMAIL, - private_key: privateKey - } + projectId: process.env.GC_PROJECT_ID, + credentials: { + client_email: process.env.GC_CLIENT_EMAIL, + private_key: privateKey, + }, }); const bucket = storage.bucket(config.gcloud.bucket); - return {hash, bucket, processFile}; -} + return { hash, bucket, processFile }; +}; const uploadGCloud = async (folder, req, res) => { try { - const {hash, bucket, processFile} = initGCloud(); + const { hash, bucket, processFile } = initGCloud(); await processFile(req, res); - let buffer = await req.file.buffer; - let filename = await req.body.filename; if (!req.file) { - return res.status(400).send({ message: "Please upload a file!" }); + return res.status(400).send({ message: 'يرجى اختيار ملف للرفع' }); } - let path = `${hash}/${folder}/${filename}`; - let blob = bucket.file(path); + const buffer = req.file.buffer; + const filename = req.body.filename; + const filePath = `${hash}/${folder}/${filename}`; + const blob = bucket.file(filePath); - console.log(path); + console.log(filePath); const blobStream = blob.createWriteStream({ resumable: false, }); - blobStream.on("error", (err) => { + blobStream.on('error', (err) => { console.log('Upload error'); console.log(err.message); res.status(500).send({ message: err.message }); @@ -140,58 +136,58 @@ const uploadGCloud = async (folder, req, res) => { console.log(`https://storage.googleapis.com/${bucket.name}/${blob.name}`); - blobStream.on("finish", async (data) => { + blobStream.on('finish', async () => { const publicUrl = format( - `https://storage.googleapis.com/${bucket.name}/${blob.name}` + `https://storage.googleapis.com/${bucket.name}/${blob.name}`, ); res.status(200).send({ - message: "Uploaded the file successfully: " + path, + message: `تم رفع الملف بنجاح: ${filePath}`, url: publicUrl, }); }); - blobStream.end(buffer) + blobStream.end(buffer); } catch (err) { console.log(err); res.status(500).send({ - message: `Could not upload the file. ${err}` + message: `تعذر رفع الملف. ${err}`, }); } -} +}; const downloadGCloud = async (req, res) => { try { - const {hash, bucket, processFile} = initGCloud(); + const { hash, bucket } = initGCloud(); - const privateUrl = await req.query.privateUrl; + const privateUrl = req.query.privateUrl; const filePath = `${hash}/${privateUrl}`; - const file = bucket.file(filePath) + const file = bucket.file(filePath); const fileExists = await file.exists(); if (fileExists[0]) { const stream = file.createReadStream(); stream.pipe(res); + return; } - else { - res.status(404).send({ - message: "Could not download the file. " + err, - }); - } + + res.status(404).send({ + message: 'تعذر العثور على الملف المطلوب تنزيله', + }); } catch (err) { res.status(404).send({ - message: "Could not download the file. " + err, + message: 'تعذر تنزيل الملف.', }); } -} +}; const deleteGCloud = async (privateUrl) => { try { - const {hash, bucket, processFile} = initGCloud(); + const { hash, bucket } = initGCloud(); const filePath = `${hash}/${privateUrl}`; - const file = bucket.file(filePath) + const file = bucket.file(filePath); const fileExists = await file.exists(); if (fileExists[0]) { @@ -200,7 +196,7 @@ const deleteGCloud = async (privateUrl) => { } catch (err) { console.log(`Cannot find the file ${privateUrl}`); } -} +}; module.exports = { initGCloud, @@ -208,6 +204,5 @@ module.exports = { downloadLocal, deleteGCloud, uploadGCloud, - downloadGCloud -} - + downloadGCloud, +}; diff --git a/backend/src/services/notifications/list.js b/backend/src/services/notifications/list.js index 2f2c45c..6de461f 100644 --- a/backend/src/services/notifications/list.js +++ b/backend/src/services/notifications/list.js @@ -1,101 +1,96 @@ const errors = { app: { - title: 'Multi-Client Detergents POS', + title: 'نظام إدارة المحل ونقطة البيع', }, auth: { - userDisabled: 'Your account is disabled', - forbidden: 'Forbidden', - unauthorized: 'Unauthorized', - userNotFound: `Sorry, we don't recognize your credentials`, - wrongPassword: `Sorry, we don't recognize your credentials`, - weakPassword: 'This password is too weak', - emailAlreadyInUse: 'Email is already in use', - invalidEmail: 'Please provide a valid email', + userDisabled: 'تم تعطيل هذا الحساب', + forbidden: 'ليس لديك صلاحية لتنفيذ هذا الإجراء', + unauthorized: 'يجب تسجيل الدخول أولاً', + userNotFound: 'البريد الإلكتروني أو كلمة المرور غير صحيحة', + wrongPassword: 'البريد الإلكتروني أو كلمة المرور غير صحيحة', + weakPassword: 'كلمة المرور ضعيفة جدًا', + emailAlreadyInUse: 'هذا البريد الإلكتروني مستخدم بالفعل', + invalidEmail: 'يرجى إدخال بريد إلكتروني صحيح', passwordReset: { - invalidToken: - 'Password reset link is invalid or has expired', - error: `Email not recognized`, + invalidToken: 'رابط إعادة تعيين كلمة المرور غير صالح أو منتهي الصلاحية', + error: 'هذا البريد الإلكتروني غير مسجل في النظام', }, passwordUpdate: { - samePassword: `You can't use the same password. Please create new password` + samePassword: 'لا يمكن استخدام كلمة المرور الحالية نفسها. اختر كلمة مرور جديدة', }, - userNotVerified: `Sorry, your email has not been verified yet`, + userNotVerified: 'لم يتم تأكيد البريد الإلكتروني لهذا الحساب بعد', emailAddressVerificationEmail: { - invalidToken: - 'Email verification link is invalid or has expired', - error: `Email not recognized`, + invalidToken: 'رابط تأكيد البريد الإلكتروني غير صالح أو منتهي الصلاحية', + error: 'هذا البريد الإلكتروني غير مسجل في النظام', }, }, iam: { errors: { - userAlreadyExists: - 'User with this email already exists', - userNotFound: 'User not found', - disablingHimself: `You can't disable yourself`, - revokingOwnPermission: `You can't revoke your own owner permission`, - deletingHimself: `You can't delete yourself`, - emailRequired: 'Email is required', + userAlreadyExists: 'يوجد مستخدم بهذا البريد الإلكتروني بالفعل', + userNotFound: 'المستخدم غير موجود', + disablingHimself: 'لا يمكنك تعطيل حسابك الحالي', + revokingOwnPermission: 'لا يمكنك إزالة صلاحية المالك من حسابك الحالي', + deletingHimself: 'لا يمكنك حذف حسابك الحالي', + emailRequired: 'البريد الإلكتروني مطلوب', }, }, importer: { errors: { - invalidFileEmpty: 'The file is empty', - invalidFileExcel: - 'Only excel (.xlsx) files are allowed', - invalidFileUpload: - 'Invalid file. Make sure you are using the last version of the template.', - importHashRequired: 'Import hash is required', - importHashExistent: 'Data has already been imported', - userEmailMissing: 'Some items in the CSV do not have an email', + invalidFileEmpty: 'الملف فارغ', + invalidFileExcel: 'يسمح فقط بملفات Excel بصيغة .xlsx', + invalidFileUpload: 'الملف غير صالح. تأكد من استخدام آخر نسخة من القالب المعتمد.', + importHashRequired: 'معرّف الاستيراد مطلوب', + importHashExistent: 'تم استيراد هذه البيانات مسبقًا', + userEmailMissing: 'بعض الصفوف في ملف CSV لا تحتوي على بريد إلكتروني', }, }, errors: { forbidden: { - message: 'Forbidden', + message: 'ليس لديك صلاحية للوصول', }, validation: { - message: 'An error occurred', + message: 'حدث خطأ في التحقق من البيانات', }, searchQueryRequired: { - message: 'Search query is required', + message: 'يرجى إدخال عبارة البحث', }, }, emails: { invitation: { - subject: `You've been invited to {0}`, - body: ` -

Hello,

-

You've been invited to {0} set password for your {1} account.

+ subject: 'تمت دعوتك إلى {0}', + body: ` +

مرحبًا،

+

تمت دعوتك إلى {0}. يرجى تعيين كلمة المرور لحسابك في {1}.

{2}

-

Thanks,

-

Your {0} team

+

شكرًا لك،

+

فريق {0}

`, }, emailAddressVerification: { - subject: `Verify your email for {0}`, + subject: 'تأكيد البريد الإلكتروني لـ {0}', body: ` -

Hello,

-

Follow this link to verify your email address.

+

مرحبًا،

+

استخدم هذا الرابط لتأكيد عنوان بريدك الإلكتروني.

{0}

-

If you didn't ask to verify this address, you can ignore this email.

-

Thanks,

-

Your {1} team

+

إذا لم تطلب هذا الإجراء، يمكنك تجاهل هذه الرسالة.

+

شكرًا لك،

+

فريق {1}

`, }, passwordReset: { - subject: `Reset your password for {0}`, + subject: 'إعادة تعيين كلمة المرور لـ {0}', body: ` -

Hello,

-

Follow this link to reset your {0} password for your {1} account.

+

مرحبًا،

+

استخدم هذا الرابط لإعادة تعيين كلمة المرور الخاصة بك في {0} لحساب {1}.

{2}

-

If you didn't ask to reset your password, you can ignore this email.

-

Thanks,

-

Your {0} team

+

إذا لم تطلب إعادة التعيين، يمكنك تجاهل هذه الرسالة.

+

شكرًا لك،

+

فريق {0}

`, }, }, diff --git a/backend/src/services/openai.js b/backend/src/services/openai.js index 3793398..0b36261 100644 --- a/backend/src/services/openai.js +++ b/backend/src/services/openai.js @@ -6,8 +6,8 @@ const loadRoleService = () => { try { return require('./roles'); } catch (error) { - console.error('Role service is missing. Advanced roles are required for this operation.', error); - const err = new Error('Role service is missing. Advanced roles are required for this operation.'); + console.error('تعذر تحميل خدمة الأدوار المطلوبة لهذه العملية.', error); + const err = new Error('تعذر تحميل خدمة الأدوار المطلوبة لهذه العملية.'); err.originalError = error; throw err; } @@ -25,17 +25,17 @@ module.exports = class OpenAiService { const { widget_id } = await response.data; await RoleService.addRoleInfo(roleId, userId, 'widgets', widget_id); return widget_id; - } else { - console.error('=======error=======', response.data); - return { value: null, error: response.data }; } + + console.error('Widget generation failed:', response.data); + return { value: null, error: response.data }; } static async askGpt(prompt) { if (!prompt) { return { success: false, - error: 'Prompt is required' + error: 'النص المطلوب للذكاء الاصطناعي فارغ', }; } @@ -59,7 +59,7 @@ module.exports = class OpenAiService { console.error('AI JSON decode failed:', error); return { success: false, - error: 'AI response parsing failed', + error: 'تعذر قراءة رد الذكاء الاصطناعي', details: error.message || String(error), }; } @@ -73,7 +73,7 @@ module.exports = class OpenAiService { console.error('AI proxy error:', response); return { success: false, - error: response.error || response.message || 'AI proxy error', + error: response.error || response.message || 'حدث خطأ أثناء التواصل مع خدمة الذكاء الاصطناعي', response, }; } diff --git a/frontend/next-env.d.ts b/frontend/next-env.d.ts index 00a8785..254b73c 100644 --- a/frontend/next-env.d.ts +++ b/frontend/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -/// +/// // NOTE: This file should not be edited // see https://nextjs.org/docs/pages/api-reference/config/typescript for more information. diff --git a/frontend/src/components/AsideMenuItem.tsx b/frontend/src/components/AsideMenuItem.tsx index dbb09b2..1b045f8 100644 --- a/frontend/src/components/AsideMenuItem.tsx +++ b/frontend/src/components/AsideMenuItem.tsx @@ -63,13 +63,13 @@ const AsideMenuItem = ({ item, isDropdownList = false }: Props) => { ) const componentClass = [ - 'flex cursor-pointer py-1.5 ', + 'flex cursor-pointer py-1.5 font-medium transition-all duration-200 ease-out', isDropdownList ? 'px-6 text-sm' : '', item.color ? getButtonColor(item.color, false, true) : `${asideMenuItemStyle}`, isLinkActive - ? `text-black ${activeLinkColor} dark:text-white dark:bg-dark-800` + ? `text-black ${activeLinkColor} shadow-sm dark:text-white dark:bg-dark-800` : '', ].join(' '); diff --git a/frontend/src/components/AsideMenuLayer.tsx b/frontend/src/components/AsideMenuLayer.tsx index 09c4acf..9356f7d 100644 --- a/frontend/src/components/AsideMenuLayer.tsx +++ b/frontend/src/components/AsideMenuLayer.tsx @@ -56,17 +56,17 @@ export default function AsideMenuLayer({ menu, className = '', ...props }: Props return (