feat(tenders): add Tender Management module (SRS, backend, frontend)
- SRS document: docs/SRS_TENDER_MANAGEMENT.md - Prisma: Tender, TenderDirective models; Deal.sourceTenderId; Attachment.tenderId/tenderDirectiveId - Backend: tenders module (CRUD, duplicate check, directives, notifications, file upload, convert-to-deal) - Frontend: tenders list, detail, create/edit forms, directives, convert to deal, i18n (en/ar), dashboard card - Seed: tenders permissions for admin and sales positions - Auth: admin.service findFirst for email check (Prisma compatibility) Made-with: Cursor
This commit is contained in:
@@ -99,12 +99,17 @@ const translations = {
|
||||
active: 'Active',
|
||||
inactive: 'Inactive',
|
||||
archived: 'Archived',
|
||||
deleted: 'Deleted'
|
||||
deleted: 'Deleted',
|
||||
all: 'All',
|
||||
view: 'View',
|
||||
showing: 'Showing',
|
||||
of: 'of'
|
||||
},
|
||||
nav: {
|
||||
dashboard: 'Dashboard',
|
||||
contacts: 'Contacts',
|
||||
crm: 'CRM',
|
||||
tenders: 'Tenders',
|
||||
projects: 'Projects',
|
||||
inventory: 'Inventory',
|
||||
hr: 'HR',
|
||||
@@ -318,6 +323,36 @@ const translations = {
|
||||
paidAmount: 'Paid Amount',
|
||||
paidDate: 'Paid Date'
|
||||
},
|
||||
tenders: {
|
||||
title: 'Tenders',
|
||||
subtitle: 'Tender Management',
|
||||
addTender: 'Add Tender',
|
||||
tenderNumber: 'Tender number',
|
||||
issuingBody: 'Issuing body',
|
||||
titleLabel: 'Title',
|
||||
termsValue: 'Terms booklet value',
|
||||
bondValue: 'Bond value',
|
||||
announcementDate: 'Announcement date',
|
||||
closingDate: 'Closing date',
|
||||
announcementLink: 'Announcement link',
|
||||
source: 'Source',
|
||||
announcementType: 'Announcement type',
|
||||
searchPlaceholder: 'Search by number, title, issuing body...',
|
||||
noTenders: 'No tenders found.',
|
||||
loadError: 'Failed to load tenders',
|
||||
createSuccess: 'Tender created successfully',
|
||||
duplicateWarning: 'Possible duplicates found. Please review.',
|
||||
directives: 'Directives',
|
||||
addDirective: 'Add directive',
|
||||
directiveType: 'Directive type',
|
||||
assignee: 'Assignee',
|
||||
convertToDeal: 'Convert to Opportunity',
|
||||
history: 'History',
|
||||
attachments: 'Attachments',
|
||||
uploadFile: 'Upload file',
|
||||
completeTask: 'Complete task',
|
||||
completionNotes: 'Completion notes'
|
||||
},
|
||||
import: {
|
||||
title: 'Import Contacts',
|
||||
downloadTemplate: 'Download Excel Template',
|
||||
@@ -381,6 +416,7 @@ const translations = {
|
||||
dashboard: 'لوحة التحكم',
|
||||
contacts: 'جهات الاتصال',
|
||||
crm: 'إدارة العملاء',
|
||||
tenders: 'المناقصات',
|
||||
projects: 'المشاريع',
|
||||
inventory: 'المخزون',
|
||||
hr: 'الموارد البشرية',
|
||||
@@ -388,6 +424,36 @@ const translations = {
|
||||
settings: 'الإعدادات',
|
||||
logout: 'تسجيل الخروج'
|
||||
},
|
||||
tenders: {
|
||||
title: 'المناقصات',
|
||||
subtitle: 'نظام إدارة المناقصات',
|
||||
addTender: 'إضافة مناقصة',
|
||||
tenderNumber: 'رقم المناقصة',
|
||||
issuingBody: 'الجهة الطارحة',
|
||||
titleLabel: 'عنوان المناقصة',
|
||||
termsValue: 'قيمة دفتر الشروط',
|
||||
bondValue: 'قيمة التأمينات',
|
||||
announcementDate: 'تاريخ الإعلان',
|
||||
closingDate: 'تاريخ الإغلاق',
|
||||
announcementLink: 'رابط الإعلان',
|
||||
source: 'مصدر المناقصة',
|
||||
announcementType: 'نوع الإعلان',
|
||||
searchPlaceholder: 'البحث بالرقم أو العنوان أو الجهة الطارحة...',
|
||||
noTenders: 'لم يتم العثور على مناقصات.',
|
||||
loadError: 'فشل تحميل المناقصات',
|
||||
createSuccess: 'تم إنشاء المناقصة بنجاح',
|
||||
duplicateWarning: 'يوجد مناقصات مشابهة. يرجى المراجعة.',
|
||||
directives: 'التوجيهات',
|
||||
addDirective: 'إضافة توجيه',
|
||||
directiveType: 'نوع التوجيه',
|
||||
assignee: 'الموظف المسؤول',
|
||||
convertToDeal: 'تحويل إلى فرصة',
|
||||
history: 'السجل',
|
||||
attachments: 'المرفقات',
|
||||
uploadFile: 'رفع ملف',
|
||||
completeTask: 'إتمام المهمة',
|
||||
completionNotes: 'ملاحظات الإنجاز'
|
||||
},
|
||||
contacts: {
|
||||
title: 'جهات الاتصال',
|
||||
addContact: 'إضافة جهة اتصال',
|
||||
|
||||
Reference in New Issue
Block a user