diff --git a/server/index.ts b/server/index.ts index fe23c60..6ed51c4 100644 --- a/server/index.ts +++ b/server/index.ts @@ -20,6 +20,7 @@ import gamehubRouter from './routers/gamehub' import escRouter from './routers/esc' import connectmeRouter from './routers/connectme' import questioneerRouter from './routers/questioneer' +import procurementRouter from './routers/procurement' import { setIo } from './io' export const app = express() @@ -105,7 +106,7 @@ const initServer = async () => { app.use("/esc", escRouter) app.use('/connectme', connectmeRouter) app.use('/questioneer', questioneerRouter) - + app.use('/procurement', procurementRouter) app.use(errorHandler) // Создаем обычный HTTP сервер diff --git a/server/routers/procurement/index.js b/server/routers/procurement/index.js new file mode 100644 index 0000000..8425a31 --- /dev/null +++ b/server/routers/procurement/index.js @@ -0,0 +1,575 @@ +const router = require('express').Router(); +const fs = require('fs'); +const path = require('path'); + +const timer = (time = 300) => (req, res, next) => setTimeout(next, time); + +// Настройка кодировки UTF-8 +router.use((req, res, next) => { + res.setHeader('Content-Type', 'application/json; charset=utf-8'); + res.setHeader('Access-Control-Allow-Origin', '*'); + res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS'); + res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization'); + next(); +}); + +router.use(timer()); + +// Загружаем моки из JSON файлов +const loadMockData = (filename) => { + try { + const filePath = path.join(__dirname, '..', 'mocks', filename); + const data = fs.readFileSync(filePath, 'utf8'); + return JSON.parse(data); + } catch (error) { + console.error(`Ошибка загрузки ${filename}:`, error); + return {}; + } +}; + +// Загружаем все моки +const userMocks = loadMockData('user.json'); +const companyMocks = loadMockData('companies.json'); +const productMocks = loadMockData('products.json'); +const searchMocks = loadMockData('search.json'); +const authMocks = loadMockData('auth.json'); + +// Логируем загруженные данные для отладки +console.log('SearchMocks loaded:', searchMocks); +console.log('Suggestions:', searchMocks.suggestions); + +// Вспомогательные функции для генерации динамических данных +const generateTimestamp = () => Date.now(); +const generateDate = (daysAgo) => new Date(Date.now() - 86400000 * daysAgo).toISOString(); + +// Функция для замены плейсхолдеров в данных +const processMockData = (data) => { + const timestamp = generateTimestamp(); + const processedData = JSON.stringify(data) + .replace(/{{timestamp}}/g, timestamp) + .replace(/{{date-(\d+)-days?}}/g, (match, days) => generateDate(parseInt(days))) + .replace(/{{date-1-day}}/g, generateDate(1)) + .replace(/{{date-2-days}}/g, generateDate(2)) + .replace(/{{date-3-days}}/g, generateDate(3)) + .replace(/{{date-4-days}}/g, generateDate(4)) + .replace(/{{date-5-days}}/g, generateDate(5)) + .replace(/{{date-6-days}}/g, generateDate(6)) + .replace(/{{date-7-days}}/g, generateDate(7)) + .replace(/{{date-8-days}}/g, generateDate(8)) + .replace(/{{date-10-days}}/g, generateDate(10)) + .replace(/{{date-12-days}}/g, generateDate(12)) + .replace(/{{date-15-days}}/g, generateDate(15)) + .replace(/{{date-18-days}}/g, generateDate(18)) + .replace(/{{date-20-days}}/g, generateDate(20)) + .replace(/{{date-21-days}}/g, generateDate(21)) + .replace(/{{date-25-days}}/g, generateDate(25)) + .replace(/{{date-28-days}}/g, generateDate(28)) + .replace(/{{date-30-days}}/g, generateDate(30)) + .replace(/{{date-35-days}}/g, generateDate(35)); + + return JSON.parse(processedData); +}; + +// Auth endpoints +router.post('/auth/login', (req, res) => { + const { email, password } = req.body; + + if (!email || !password) { + return res.status(400).json({ + error: 'Validation failed', + message: authMocks.errorMessages?.validationFailed || 'Email и пароль обязательны' + }); + } + + // Имитация неверных учетных данных + if (password === 'wrong') { + return res.status(401).json({ + error: 'Unauthorized', + message: authMocks.errorMessages?.invalidCredentials || 'Неверный email или пароль' + }); + } + + const authResponse = processMockData(authMocks.mockAuthResponse); + res.status(200).json(authResponse); +}); + +router.post('/auth/register', (req, res) => { + const { email, password, inn, agreeToTerms } = req.body; + + if (!email || !password || !inn) { + return res.status(400).json({ + error: 'Validation failed', + message: authMocks.errorMessages?.validationFailed || 'Заполните все обязательные поля' + }); + } + + if (!agreeToTerms) { + return res.status(400).json({ + error: 'Validation failed', + message: authMocks.errorMessages?.termsRequired || 'Необходимо принять условия использования' + }); + } + + // Создаем нового пользователя с данными из регистрации + const newUser = { + id: 'user-' + generateTimestamp(), + email: email, + firstName: req.body.firstName || 'Иван', + lastName: req.body.lastName || 'Петров', + position: req.body.position || 'Директор' + }; + + const newCompany = { + id: 'company-' + generateTimestamp(), + name: req.body.fullName || companyMocks.mockCompany?.name, + inn: req.body.inn, + ogrn: req.body.ogrn || companyMocks.mockCompany?.ogrn, + fullName: req.body.fullName || companyMocks.mockCompany?.fullName, + shortName: req.body.shortName, + legalForm: req.body.legalForm || 'ООО', + industry: req.body.industry || 'Другое', + companySize: req.body.companySize || '1-10', + website: req.body.website || '', + verified: false, + rating: 0 + }; + + res.status(201).json({ + user: newUser, + company: newCompany, + tokens: { + accessToken: 'mock-access-token-' + generateTimestamp(), + refreshToken: 'mock-refresh-token-' + generateTimestamp() + } + }); +}); + +router.post('/auth/logout', (req, res) => { + res.status(200).json({ + message: authMocks.successMessages?.logoutSuccess || 'Успешный выход' + }); +}); + +router.post('/auth/refresh', (req, res) => { + const { refreshToken } = req.body; + + if (!refreshToken) { + return res.status(401).json({ + error: 'Unauthorized', + message: authMocks.errorMessages?.refreshTokenRequired || 'Refresh token обязателен' + }); + } + + res.status(200).json({ + accessToken: 'mock-access-token-refreshed-' + generateTimestamp(), + refreshToken: 'mock-refresh-token-refreshed-' + generateTimestamp() + }); +}); + +router.get('/auth/verify-email/:token', (req, res) => { + res.status(200).json({ + message: authMocks.successMessages?.emailVerified || 'Email успешно подтвержден' + }); +}); + +router.post('/auth/request-password-reset', (req, res) => { + const { email } = req.body; + + if (!email) { + return res.status(400).json({ + error: 'Validation failed', + message: authMocks.errorMessages?.emailRequired || 'Email обязателен' + }); + } + + res.status(200).json({ + message: authMocks.successMessages?.passwordResetSent || 'Письмо для восстановления пароля отправлено' + }); +}); + +router.post('/auth/reset-password', (req, res) => { + const { token, newPassword } = req.body; + + if (!token || !newPassword) { + return res.status(400).json({ + error: 'Validation failed', + message: authMocks.errorMessages?.validationFailed || 'Token и новый пароль обязательны' + }); + } + + res.status(200).json({ + message: authMocks.successMessages?.passwordResetSuccess || 'Пароль успешно изменен' + }); +}); + +// Companies endpoints +router.get('/companies/my/stats', (req, res) => { + res.status(200).json({ + profileViews: 142, + profileViewsChange: 12, + sentRequests: 8, + sentRequestsChange: 2, + receivedRequests: 15, + receivedRequestsChange: 5, + newMessages: 3, + rating: 4.5 + }); +}); + +router.get('/companies/:id', (req, res) => { + const company = processMockData(companyMocks.mockCompany); + res.status(200).json(company); +}); + +router.patch('/companies/:id', (req, res) => { + const updatedCompany = { + ...processMockData(companyMocks.mockCompany), + ...req.body, + id: req.params.id + }; + + res.status(200).json(updatedCompany); +}); + +router.get('/companies/:id/stats', (req, res) => { + res.status(200).json({ + profileViews: 142, + profileViewsChange: 12, + sentRequests: 8, + sentRequestsChange: 2, + receivedRequests: 15, + receivedRequestsChange: 5, + newMessages: 3, + rating: 4.5 + }); +}); + +router.post('/companies/:id/logo', (req, res) => { + res.status(200).json({ + logoUrl: 'https://via.placeholder.com/200x200/4299E1/FFFFFF?text=Logo' + }); +}); + +router.get('/companies/check-inn/:inn', (req, res) => { + const inn = req.params.inn; + + // Имитация проверки ИНН + if (inn.length !== 10 && inn.length !== 12) { + return res.status(400).json({ + error: 'Validation failed', + message: authMocks.errorMessages?.innValidation || 'ИНН должен содержать 10 или 12 цифр' + }); + } + + const mockINNData = companyMocks.mockINNData || {}; + const companyData = mockINNData[inn] || { + name: 'ОБЩЕСТВО С ОГРАНИЧЕННОЙ ОТВЕТСТВЕННОСТЬЮ "ТЕСТОВАЯ КОМПАНИЯ ' + inn + '"', + ogrn: '10277' + inn, + legal_form: 'ООО' + }; + + res.status(200).json({ data: companyData }); +}); + +// Products endpoints +router.get('/products/my', (req, res) => { + const products = processMockData(productMocks.mockProducts); + res.status(200).json(products); +}); + +router.get('/products', (req, res) => { + const products = processMockData(productMocks.mockProducts); + res.status(200).json({ + items: products, + total: products.length, + page: 1, + pageSize: 20 + }); +}); + +router.post('/products', (req, res) => { + const newProduct = { + id: 'prod-' + generateTimestamp(), + ...req.body, + companyId: 'company-123', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString() + }; + + res.status(201).json(newProduct); +}); + +router.get('/products/:id', (req, res) => { + const products = processMockData(productMocks.mockProducts); + const product = products.find(p => p.id === req.params.id); + + if (product) { + res.status(200).json(product); + } else { + res.status(200).json({ + id: req.params.id, + name: 'Продукт ' + req.params.id, + description: 'Описание продукта', + category: 'Категория', + type: 'sell', + companyId: 'company-123', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString() + }); + } +}); + +router.patch('/products/:id', (req, res) => { + const products = processMockData(productMocks.mockProducts); + const product = products.find(p => p.id === req.params.id); + + const updatedProduct = { + ...(product || {}), + ...req.body, + id: req.params.id, + updatedAt: new Date().toISOString() + }; + + res.status(200).json(updatedProduct); +}); + +router.delete('/products/:id', (req, res) => { + res.status(204).send(); +}); + +// Тестовый endpoint для проверки данных +router.get('/test-data', (req, res) => { + res.status(200).json({ + companiesCount: companyMocks.mockCompanies?.length || 0, + suggestionsCount: searchMocks.suggestions?.length || 0, + firstCompany: companyMocks.mockCompanies?.[0] || null, + firstSuggestion: searchMocks.suggestions?.[0] || null, + allSuggestions: searchMocks.suggestions || [] + }); +}); +router.get('/search', (req, res) => { + const { + query, + industries, + companySize, + geography, + minRating, + type, + sortBy = 'relevance', + sortOrder = 'desc', + page = 1, + limit = 20 + } = req.query; + + console.log('Search query:', query); + console.log('Search params:', req.query); + + const companies = processMockData(companyMocks.mockCompanies); + console.log('Companies loaded:', companies.length); + console.log('First company:', companies[0]); + + let filtered = [...companies]; + + // Поиск по тексту + if (query) { + const q = query.toLowerCase().trim(); + console.log('Searching for:', q); + + filtered = filtered.filter(c => { + const fullName = (c.fullName || '').toLowerCase(); + const shortName = (c.shortName || '').toLowerCase(); + const industry = (c.industry || '').toLowerCase(); + const slogan = (c.slogan || '').toLowerCase(); + const legalAddress = (c.legalAddress || '').toLowerCase(); + + const matches = fullName.includes(q) || + shortName.includes(q) || + industry.includes(q) || + slogan.includes(q) || + legalAddress.includes(q); + + if (matches) { + console.log('Found match:', c.shortName, 'in', { fullName, shortName, industry, slogan, legalAddress }); + } + + return matches; + }); + + console.log('Filtered results:', filtered.length); + } + + // Фильтр по отраслям + if (industries && industries.length > 0) { + const industriesArray = Array.isArray(industries) ? industries : [industries]; + filtered = filtered.filter(c => industriesArray.includes(c.industry)); + } + + // Фильтр по размеру компании + if (companySize && companySize.length > 0) { + const sizeArray = Array.isArray(companySize) ? companySize : [companySize]; + filtered = filtered.filter(c => sizeArray.includes(c.companySize)); + } + + // Фильтр по рейтингу + if (minRating) { + filtered = filtered.filter(c => c.rating >= parseFloat(minRating)); + } + + // Сортировка + filtered.sort((a, b) => { + let comparison = 0; + + switch (sortBy) { + case 'rating': + comparison = a.rating - b.rating; + break; + case 'name': + comparison = (a.shortName || a.fullName).localeCompare(b.shortName || b.fullName); + break; + case 'relevance': + default: + // Для релевантности используем рейтинг как основной критерий + comparison = a.rating - b.rating; + break; + } + + return sortOrder === 'asc' ? comparison : -comparison; + }); + + const total = filtered.length; + const totalPages = Math.ceil(total / limit); + const startIndex = (page - 1) * limit; + const endIndex = startIndex + parseInt(limit); + const paginatedResults = filtered.slice(startIndex, endIndex); + + res.status(200).json({ + companies: paginatedResults, + total, + page: parseInt(page), + totalPages + }); +}); + +router.post('/search/ai', (req, res) => { + const { query } = req.body; + + // Простая логика AI поиска на основе ключевых слов + const companies = processMockData(companyMocks.mockCompanies); + let aiResults = [...companies]; + const q = query.toLowerCase(); + + // Определяем приоритетные отрасли на основе запроса + if (q.includes('строитель') || q.includes('строй') || q.includes('дом') || q.includes('здание')) { + aiResults = aiResults.filter(c => c.industry === 'Строительство'); + } else if (q.includes('металл') || q.includes('сталь') || q.includes('производств') || q.includes('завод')) { + aiResults = aiResults.filter(c => c.industry === 'Производство'); + } else if (q.includes('логистик') || q.includes('доставк') || q.includes('транспорт')) { + aiResults = aiResults.filter(c => c.industry === 'Логистика'); + } else if (q.includes('торговл') || q.includes('продаж') || q.includes('снабжени')) { + aiResults = aiResults.filter(c => c.industry === 'Торговля'); + } else if (q.includes('it') || q.includes('программ') || q.includes('технолог') || q.includes('софт')) { + aiResults = aiResults.filter(c => c.industry === 'IT'); + } else if (q.includes('услуг') || q.includes('консалт') || q.includes('помощь')) { + aiResults = aiResults.filter(c => c.industry === 'Услуги'); + } + + // Сортируем по рейтингу и берем топ-5 + aiResults.sort((a, b) => b.rating - a.rating); + const topResults = aiResults.slice(0, 5); + + // Генерируем AI предложение + let aiSuggestion = `На основе вашего запроса "${query}" мы нашли ${topResults.length} подходящих партнеров. `; + + if (topResults.length > 0) { + const industries = [...new Set(topResults.map(c => c.industry))]; + aiSuggestion += `Рекомендуем обратить внимание на компании в сфере ${industries.join(', ')}. `; + aiSuggestion += `Все предложенные партнеры имеют высокий рейтинг (от ${Math.min(...topResults.map(c => c.rating)).toFixed(1)} до ${Math.max(...topResults.map(c => c.rating)).toFixed(1)}) и подтвержденный статус.`; + } else { + aiSuggestion += 'Попробуйте изменить формулировку запроса или использовать фильтры для более точного поиска.'; + } + + res.status(200).json({ + companies: topResults, + total: topResults.length, + page: 1, + totalPages: 1, + aiSuggestion + }); +}); + +router.get('/search/suggestions', (req, res) => { + const { q } = req.query; + + const suggestions = searchMocks.suggestions || []; + console.log('Suggestions loaded:', suggestions); + console.log('Query:', q); + + const filtered = q + ? suggestions.filter(s => s.toLowerCase().includes(q.toLowerCase())) + : suggestions.slice(0, 10); // Показываем только первые 10 если нет запроса + + console.log('Filtered suggestions:', filtered); + res.status(200).json(filtered); +}); + +router.get('/search/recommendations', (req, res) => { + // Динамически генерируем рекомендации на основе топовых компаний + const companies = processMockData(companyMocks.mockCompanies); + const topCompanies = companies + .filter(c => c.verified && c.rating >= 4.5) + .sort((a, b) => b.rating - a.rating) + .slice(0, 6); + + const recommendations = topCompanies.map(company => ({ + id: company.id, + name: company.shortName || company.fullName, + industry: company.industry, + logo: company.logo, + matchScore: Math.floor(company.rating * 20), // Конвертируем рейтинг в проценты + reason: getRecommendationReason(company, searchMocks.recommendationReasons) + })); + + res.status(200).json(recommendations); +}); + +// Вспомогательная функция для генерации причин рекомендаций +function getRecommendationReason(company, reasons) { + return reasons?.[company.industry] || 'Проверенный партнер с высоким рейтингом'; +} + +router.get('/search/history', (req, res) => { + const history = processMockData(searchMocks.searchHistory); + res.status(200).json(history); +}); + +router.get('/search/saved', (req, res) => { + const savedSearches = processMockData(searchMocks.savedSearches); + res.status(200).json(savedSearches); +}); + +router.post('/search/saved', (req, res) => { + const { name, params } = req.body; + + res.status(201).json({ + id: 'saved-' + generateTimestamp(), + name, + params, + createdAt: new Date().toISOString() + }); +}); + +router.delete('/search/saved/:id', (req, res) => { + res.status(204).send(); +}); + +router.post('/search/favorites/:companyId', (req, res) => { + res.status(200).json({ + message: authMocks.successMessages?.addedToFavorites || 'Добавлено в избранное' + }); +}); + +router.delete('/search/favorites/:companyId', (req, res) => { + res.status(204).send(); +}); + +module.exports = router; \ No newline at end of file diff --git a/server/routers/procurement/mocks/auth.json b/server/routers/procurement/mocks/auth.json new file mode 100644 index 0000000..eabb81d --- /dev/null +++ b/server/routers/procurement/mocks/auth.json @@ -0,0 +1,46 @@ +{ + "mockAuthResponse": { + "user": { + "id": "user-123", + "email": "test@company.com", + "firstName": "Иван", + "lastName": "Петров", + "position": "Генеральный директор" + }, + "company": { + "id": "company-123", + "name": "ООО \"Тестовая Компания\"", + "inn": "7707083893", + "ogrn": "1027700132195", + "fullName": "Общество с ограниченной ответственностью \"Тестовая Компания\"", + "shortName": "ООО \"Тест\"", + "legalForm": "ООО", + "industry": "Производство", + "companySize": "50-100", + "website": "https://test-company.ru", + "verified": true, + "rating": 4.5 + }, + "tokens": { + "accessToken": "mock-access-token-{{timestamp}}", + "refreshToken": "mock-refresh-token-{{timestamp}}" + } + }, + "errorMessages": { + "validationFailed": "Заполните все обязательные поля", + "emailRequired": "Email обязателен", + "passwordRequired": "Пароль обязателен", + "termsRequired": "Необходимо принять условия использования", + "invalidCredentials": "Неверный email или пароль", + "refreshTokenRequired": "Refresh token обязателен", + "innValidation": "ИНН должен содержать 10 или 12 цифр" + }, + "successMessages": { + "logoutSuccess": "Успешный выход", + "emailVerified": "Email успешно подтвержден", + "passwordResetSent": "Письмо для восстановления пароля отправлено", + "passwordResetSuccess": "Пароль успешно изменен", + "logoUploaded": "Логотип успешно загружен", + "addedToFavorites": "Добавлено в избранное" + } +} diff --git a/server/routers/procurement/mocks/companies.json b/server/routers/procurement/mocks/companies.json new file mode 100644 index 0000000..8e3bffe --- /dev/null +++ b/server/routers/procurement/mocks/companies.json @@ -0,0 +1,430 @@ +{ + "mockCompany": { + "id": "company-123", + "name": "ООО \"Тестовая Компания\"", + "inn": "7707083893", + "ogrn": "1027700132195", + "fullName": "Общество с ограниченной ответственностью \"Тестовая Компания\"", + "shortName": "ООО \"Тест\"", + "legalForm": "ООО", + "industry": "Производство", + "companySize": "50-100", + "website": "https://test-company.ru", + "verified": true, + "rating": 4.5 + }, + "mockINNData": { + "7707083893": { + "name": "ПУБЛИЧНОЕ АКЦИОНЕРНОЕ ОБЩЕСТВО \"СБЕРБАНК РОССИИ\"", + "ogrn": "1027700132195", + "legal_form": "ПАО" + }, + "7730048036": { + "name": "ОБЩЕСТВО С ОГРАНИЧЕННОЙ ОТВЕТСТВЕННОСТЬЮ \"КОМПАНИЯ\"", + "ogrn": "1047730048036", + "legal_form": "ООО" + } + }, + "mockCompanies": [ + { + "id": "company-1", + "inn": "7707083893", + "ogrn": "1027700132195", + "fullName": "Общество с ограниченной ответственностью \"СтройКомплект\"", + "shortName": "ООО \"СтройКомплект\"", + "legalForm": "ООО", + "industry": "Строительство", + "companySize": "100-250", + "website": "https://stroykomplekt.ru", + "logo": "https://via.placeholder.com/100x100/2B6CB0/FFFFFF?text=SK", + "slogan": "Строим будущее вместе", + "rating": 4.8, + "verified": true, + "phone": "+7 (495) 123-45-67", + "email": "info@stroykomplekt.ru", + "legalAddress": "г. Москва, ул. Строительная, д. 15", + "foundedYear": 2010, + "employeeCount": "150 сотрудников" + }, + { + "id": "company-6", + "inn": "7707083894", + "ogrn": "1027700132196", + "fullName": "Акционерное общество \"Московский Строй\"", + "shortName": "АО \"Московский Строй\"", + "legalForm": "АО", + "industry": "Строительство", + "companySize": "500+", + "website": "https://moscow-stroy.ru", + "logo": "https://via.placeholder.com/100x100/1A365D/FFFFFF?text=MS", + "slogan": "Качество и надежность с 1995 года", + "rating": 4.9, + "verified": true, + "phone": "+7 (495) 987-65-43", + "email": "info@moscow-stroy.ru", + "legalAddress": "г. Москва, пр. Мира, д. 100", + "foundedYear": 1995, + "employeeCount": "800+ сотрудников" + }, + { + "id": "company-7", + "inn": "7707083895", + "ogrn": "1027700132197", + "fullName": "Общество с ограниченной ответственностью \"ДомСтрой\"", + "shortName": "ООО \"ДомСтрой\"", + "legalForm": "ООО", + "industry": "Строительство", + "companySize": "50-100", + "website": "https://domstroy.ru", + "logo": "https://via.placeholder.com/100x100/2D3748/FFFFFF?text=DS", + "slogan": "Строим дома мечты", + "rating": 4.3, + "verified": true, + "phone": "+7 (495) 555-12-34", + "email": "info@domstroy.ru", + "legalAddress": "г. Москва, ул. Жилстроительная, д. 25", + "foundedYear": 2015, + "employeeCount": "75 сотрудников" + }, + { + "id": "company-4", + "inn": "7730048038", + "ogrn": "1047730048038", + "fullName": "Общество с ограниченной ответственностью \"МеталлПром\"", + "shortName": "ООО \"МеталлПром\"", + "legalForm": "ООО", + "industry": "Производство", + "companySize": "250-500", + "website": "https://metallprom.ru", + "logo": "https://via.placeholder.com/100x100/E53E3E/FFFFFF?text=MP", + "slogan": "Металл высшего качества", + "rating": 4.7, + "verified": true, + "phone": "+7 (495) 456-78-90", + "email": "info@metallprom.ru", + "legalAddress": "г. Москва, ул. Промышленная, д. 50", + "foundedYear": 2008, + "employeeCount": "300 сотрудников" + }, + { + "id": "company-8", + "inn": "7730048040", + "ogrn": "1047730048040", + "fullName": "Общество с ограниченной ответственностью \"СтальМет\"", + "shortName": "ООО \"СтальМет\"", + "legalForm": "ООО", + "industry": "Производство", + "companySize": "100-250", + "website": "https://stalmet.ru", + "logo": "https://via.placeholder.com/100x100/9C4221/FFFFFF?text=SM", + "slogan": "Сталь для промышленности", + "rating": 4.6, + "verified": true, + "phone": "+7 (495) 777-88-99", + "email": "sales@stalmet.ru", + "legalAddress": "г. Москва, ул. Металлургическая, д. 30", + "foundedYear": 2012, + "employeeCount": "180 сотрудников" + }, + { + "id": "company-9", + "inn": "7730048041", + "ogrn": "1047730048041", + "fullName": "Общество с ограниченной ответственностью \"ПластМаш\"", + "shortName": "ООО \"ПластМаш\"", + "legalForm": "ООО", + "industry": "Производство", + "companySize": "50-100", + "website": "https://plastmash.ru", + "logo": "https://via.placeholder.com/100x100/38A169/FFFFFF?text=PM", + "slogan": "Пластиковые изделия для всех отраслей", + "rating": 4.4, + "verified": true, + "phone": "+7 (495) 333-44-55", + "email": "info@plastmash.ru", + "legalAddress": "г. Москва, ул. Пластиковая, д. 12", + "foundedYear": 2018, + "employeeCount": "80 сотрудников" + }, + { + "id": "company-2", + "inn": "7730048036", + "ogrn": "1047730048036", + "fullName": "Общество с ограниченной ответственностью \"ТехСнаб\"", + "shortName": "ООО \"ТехСнаб\"", + "legalForm": "ООО", + "industry": "Торговля", + "companySize": "50-100", + "website": "https://techsnab.ru", + "logo": "https://via.placeholder.com/100x100/38A169/FFFFFF?text=TS", + "slogan": "Снабжение для профессионалов", + "rating": 4.5, + "verified": true, + "phone": "+7 (495) 234-56-78", + "email": "sales@techsnab.ru", + "legalAddress": "г. Москва, ул. Торговая, д. 8", + "foundedYear": 2010, + "employeeCount": "90 сотрудников" + }, + { + "id": "company-10", + "inn": "7730048042", + "ogrn": "1047730048042", + "fullName": "Общество с ограниченной ответственностью \"ОптТорг\"", + "shortName": "ООО \"ОптТорг\"", + "legalForm": "ООО", + "industry": "Торговля", + "companySize": "100-250", + "website": "https://opttorg.ru", + "logo": "https://via.placeholder.com/100x100/805AD5/FFFFFF?text=OT", + "slogan": "Оптовые поставки по всей России", + "rating": 4.2, + "verified": true, + "phone": "+7 (495) 111-22-33", + "email": "info@opttorg.ru", + "legalAddress": "г. Москва, ул. Оптовая, д. 45", + "foundedYear": 2005, + "employeeCount": "200 сотрудников" + }, + { + "id": "company-5", + "inn": "7730048039", + "ogrn": "1047730048039", + "fullName": "Общество с ограниченной ответственностью \"ЛогистикПлюс\"", + "shortName": "ООО \"ЛогистикПлюс\"", + "legalForm": "ООО", + "industry": "Логистика", + "companySize": "100-250", + "website": "https://logistikplus.ru", + "logo": "https://via.placeholder.com/100x100/805AD5/FFFFFF?text=LP", + "slogan": "Доставляем быстро и надежно", + "rating": 4.6, + "verified": true, + "phone": "+7 (495) 567-89-01", + "email": "info@logistikplus.ru", + "legalAddress": "г. Москва, ул. Логистическая, д. 20", + "foundedYear": 2013, + "employeeCount": "150 сотрудников" + }, + { + "id": "company-11", + "inn": "7730048043", + "ogrn": "1047730048043", + "fullName": "Общество с ограниченной ответственностью \"ТрансЛогист\"", + "shortName": "ООО \"ТрансЛогист\"", + "legalForm": "ООО", + "industry": "Логистика", + "companySize": "250-500", + "website": "https://translogist.ru", + "logo": "https://via.placeholder.com/100x100/2B6CB0/FFFFFF?text=TL", + "slogan": "Транспортные решения для бизнеса", + "rating": 4.8, + "verified": true, + "phone": "+7 (495) 999-88-77", + "email": "info@translogist.ru", + "legalAddress": "г. Москва, ул. Транспортная, д. 60", + "foundedYear": 2007, + "employeeCount": "350 сотрудников" + }, + { + "id": "company-12", + "inn": "7730048044", + "ogrn": "1047730048044", + "fullName": "Общество с ограниченной ответственностью \"ТехСофт\"", + "shortName": "ООО \"ТехСофт\"", + "legalForm": "ООО", + "industry": "IT", + "companySize": "50-100", + "website": "https://techsoft.ru", + "logo": "https://via.placeholder.com/100x100/3182CE/FFFFFF?text=TS", + "slogan": "IT-решения для бизнеса", + "rating": 4.7, + "verified": true, + "phone": "+7 (495) 444-55-66", + "email": "info@techsoft.ru", + "legalAddress": "г. Москва, ул. Программистов, д. 10", + "foundedYear": 2016, + "employeeCount": "85 сотрудников" + }, + { + "id": "company-13", + "inn": "7730048045", + "ogrn": "1047730048045", + "fullName": "Общество с ограниченной ответственностью \"КиберТех\"", + "shortName": "ООО \"КиберТех\"", + "legalForm": "ООО", + "industry": "IT", + "companySize": "100-250", + "website": "https://cybertech.ru", + "logo": "https://via.placeholder.com/100x100/553C9A/FFFFFF?text=CT", + "slogan": "Кибербезопасность и автоматизация", + "rating": 4.9, + "verified": true, + "phone": "+7 (495) 666-77-88", + "email": "info@cybertech.ru", + "legalAddress": "г. Москва, ул. Кибернетическая, д. 5", + "foundedYear": 2014, + "employeeCount": "120 сотрудников" + }, + { + "id": "company-3", + "inn": "7730048037", + "ogrn": "1047730048037", + "fullName": "Индивидуальный предприниматель Сидоров Петр Иванович", + "shortName": "ИП Сидоров П.И.", + "legalForm": "ИП", + "industry": "Услуги", + "companySize": "1-10", + "website": "https://sidorov-service.ru", + "logo": "https://via.placeholder.com/100x100/D69E2E/FFFFFF?text=SI", + "slogan": "Качественные услуги для малого бизнеса", + "rating": 4.2, + "verified": false, + "phone": "+7 (495) 345-67-89", + "email": "info@sidorov-service.ru", + "legalAddress": "г. Москва, ул. Сервисная, д. 3", + "foundedYear": 2020, + "employeeCount": "5 сотрудников" + }, + { + "id": "company-14", + "inn": "7730048046", + "ogrn": "1047730048046", + "fullName": "Общество с ограниченной ответственностью \"КонсалтПро\"", + "shortName": "ООО \"КонсалтПро\"", + "legalForm": "ООО", + "industry": "Услуги", + "companySize": "10-50", + "website": "https://konsultpro.ru", + "logo": "https://via.placeholder.com/100x100/38A169/FFFFFF?text=KP", + "slogan": "Консалтинг для роста бизнеса", + "rating": 4.5, + "verified": true, + "phone": "+7 (495) 222-33-44", + "email": "info@konsultpro.ru", + "legalAddress": "г. Москва, ул. Консультационная, д. 15", + "foundedYear": 2017, + "employeeCount": "25 сотрудников" + }, + { + "id": "company-15", + "inn": "7730048047", + "ogrn": "1047730048047", + "fullName": "Общество с ограниченной ответственностью \"ПищеПром\"", + "shortName": "ООО \"ПищеПром\"", + "legalForm": "ООО", + "industry": "Пищевая промышленность", + "companySize": "100-250", + "website": "https://pishcheprom.ru", + "logo": "https://via.placeholder.com/100x100/38A169/FFFFFF?text=PP", + "slogan": "Качественные продукты питания", + "rating": 4.4, + "verified": true, + "phone": "+7 (495) 888-99-00", + "email": "info@pishcheprom.ru", + "legalAddress": "г. Москва, ул. Пищевая, д. 40", + "foundedYear": 2011, + "employeeCount": "180 сотрудников" + }, + { + "id": "company-16", + "inn": "7730048048", + "ogrn": "1047730048048", + "fullName": "Общество с ограниченной ответственностью \"ЭнергоСервис\"", + "shortName": "ООО \"ЭнергоСервис\"", + "legalForm": "ООО", + "industry": "Энергетика", + "companySize": "50-100", + "website": "https://energoservice.ru", + "logo": "https://via.placeholder.com/100x100/F6AD55/FFFFFF?text=ES", + "slogan": "Энергетические решения", + "rating": 4.6, + "verified": true, + "phone": "+7 (495) 555-66-77", + "email": "info@energoservice.ru", + "legalAddress": "г. Москва, ул. Энергетическая, д. 25", + "foundedYear": 2013, + "employeeCount": "70 сотрудников" + }, + { + "id": "company-17", + "inn": "7730048049", + "ogrn": "1047730048049", + "fullName": "Общество с ограниченной ответственностью \"МедТех\"", + "shortName": "ООО \"МедТех\"", + "legalForm": "ООО", + "industry": "Медицина", + "companySize": "100-250", + "website": "https://medtech.ru", + "logo": "https://via.placeholder.com/100x100/E53E3E/FFFFFF?text=MT", + "slogan": "Медицинские технологии будущего", + "rating": 4.8, + "verified": true, + "phone": "+7 (495) 777-00-11", + "email": "info@medtech.ru", + "legalAddress": "г. Москва, ул. Медицинская, д. 35", + "foundedYear": 2015, + "employeeCount": "200 сотрудников" + }, + { + "id": "company-18", + "inn": "7730048050", + "ogrn": "1047730048050", + "fullName": "Общество с ограниченной ответственностью \"ОбразЦентр\"", + "shortName": "ООО \"ОбразЦентр\"", + "legalForm": "ООО", + "industry": "Образование", + "companySize": "50-100", + "website": "https://obrazcentr.ru", + "logo": "https://via.placeholder.com/100x100/38A169/FFFFFF?text=OC", + "slogan": "Образование и развитие персонала", + "rating": 4.3, + "verified": true, + "phone": "+7 (495) 333-00-22", + "email": "info@obrazcentr.ru", + "legalAddress": "г. Москва, ул. Образовательная, д. 18", + "foundedYear": 2018, + "employeeCount": "60 сотрудников" + }, + { + "id": "company-19", + "inn": "7730048051", + "ogrn": "1047730048051", + "fullName": "Общество с ограниченной ответственностью \"ФинКонсалт\"", + "shortName": "ООО \"ФинКонсалт\"", + "legalForm": "ООО", + "industry": "Финансы", + "companySize": "10-50", + "website": "https://finkonsalt.ru", + "logo": "https://via.placeholder.com/100x100/2B6CB0/FFFFFF?text=FK", + "slogan": "Финансовое консультирование", + "rating": 4.7, + "verified": true, + "phone": "+7 (495) 444-00-33", + "email": "info@finkonsalt.ru", + "legalAddress": "г. Москва, ул. Финансовая, д. 12", + "foundedYear": 2016, + "employeeCount": "35 сотрудников" + }, + { + "id": "company-20", + "inn": "7730048052", + "ogrn": "1047730048052", + "fullName": "Общество с ограниченной ответственностью \"АгроТех\"", + "shortName": "ООО \"АгроТех\"", + "legalForm": "ООО", + "industry": "Сельское хозяйство", + "companySize": "100-250", + "website": "https://agrotech.ru", + "logo": "https://via.placeholder.com/100x100/38A169/FFFFFF?text=AT", + "slogan": "Современные технологии в сельском хозяйстве", + "rating": 4.5, + "verified": true, + "phone": "+7 (495) 666-00-44", + "email": "info@agrotech.ru", + "legalAddress": "г. Москва, ул. Аграрная, д. 28", + "foundedYear": 2012, + "employeeCount": "160 сотрудников" + } + ] +} diff --git a/server/routers/procurement/mocks/products.json b/server/routers/procurement/mocks/products.json new file mode 100644 index 0000000..9b9b9a6 --- /dev/null +++ b/server/routers/procurement/mocks/products.json @@ -0,0 +1,158 @@ +{ + "mockProducts": [ + { + "id": "prod-1", + "name": "Металлические конструкции", + "description": "Производство и поставка металлических конструкций любой сложности для строительства", + "category": "Строительные материалы", + "type": "sell", + "companyId": "company-4", + "price": "от 50 000 руб/тонна", + "createdAt": "{{date-10-days}}", + "updatedAt": "{{date-2-days}}" + }, + { + "id": "prod-2", + "name": "Стальные балки и профили", + "description": "Высококачественные стальные балки и профили для промышленного строительства", + "category": "Металлопрокат", + "type": "sell", + "companyId": "company-8", + "price": "от 45 000 руб/тонна", + "createdAt": "{{date-8-days}}", + "updatedAt": "{{date-1-day}}" + }, + { + "id": "prod-3", + "name": "Пластиковые изделия", + "description": "Производство пластиковых изделий для различных отраслей промышленности", + "category": "Пластик", + "type": "sell", + "companyId": "company-9", + "price": "от 200 руб/кг", + "createdAt": "{{date-15-days}}", + "updatedAt": "{{date-3-days}}" + }, + { + "id": "prod-4", + "name": "Строительные материалы", + "description": "Полный спектр строительных материалов для жилого и коммерческого строительства", + "category": "Строительные материалы", + "type": "sell", + "companyId": "company-1", + "price": "по запросу", + "createdAt": "{{date-20-days}}", + "updatedAt": "{{date-5-days}}" + }, + { + "id": "prod-5", + "name": "IT-решения для бизнеса", + "description": "Разработка программного обеспечения и IT-консалтинг для предприятий", + "category": "IT-услуги", + "type": "sell", + "companyId": "company-12", + "price": "от 100 000 руб/проект", + "createdAt": "{{date-12-days}}", + "updatedAt": "{{date-2-days}}" + }, + { + "id": "prod-6", + "name": "Логистические услуги", + "description": "Комплексные логистические услуги по всей России и СНГ", + "category": "Логистика", + "type": "sell", + "companyId": "company-5", + "price": "от 15 руб/км", + "createdAt": "{{date-18-days}}", + "updatedAt": "{{date-4-days}}" + }, + { + "id": "prod-7", + "name": "Пищевая продукция", + "description": "Производство качественных продуктов питания для HoReCa и розничной торговли", + "category": "Пищевая продукция", + "type": "sell", + "companyId": "company-15", + "price": "по прайс-листу", + "createdAt": "{{date-25-days}}", + "updatedAt": "{{date-7-days}}" + }, + { + "id": "prod-8", + "name": "Медицинское оборудование", + "description": "Поставка современного медицинского оборудования и расходных материалов", + "category": "Медицинское оборудование", + "type": "sell", + "companyId": "company-17", + "price": "по запросу", + "createdAt": "{{date-30-days}}", + "updatedAt": "{{date-10-days}}" + }, + { + "id": "prod-9", + "name": "Запчасти для спецтехники", + "description": "Ищем надежного поставщика запчастей для строительной техники Caterpillar, Komatsu", + "category": "Запчасти", + "type": "buy", + "companyId": "company-2", + "budget": "до 500 000 руб", + "createdAt": "{{date-5-days}}", + "updatedAt": "{{date-1-day}}" + }, + { + "id": "prod-10", + "name": "Сырье для производства", + "description": "Требуется качественное сырье для производства пластиковых изделий", + "category": "Сырье", + "type": "buy", + "companyId": "company-9", + "budget": "до 1 000 000 руб", + "createdAt": "{{date-7-days}}", + "updatedAt": "{{date-2-days}}" + }, + { + "id": "prod-11", + "name": "IT-оборудование", + "description": "Закупка серверного оборудования и сетевого оборудования для офиса", + "category": "IT-оборудование", + "type": "buy", + "companyId": "company-13", + "budget": "до 2 000 000 руб", + "createdAt": "{{date-3-days}}", + "updatedAt": "{{date-1-day}}" + }, + { + "id": "prod-12", + "name": "Консалтинговые услуги", + "description": "Требуется консультация по оптимизации бизнес-процессов", + "category": "Консалтинг", + "type": "buy", + "companyId": "company-14", + "budget": "до 300 000 руб", + "createdAt": "{{date-4-days}}", + "updatedAt": "{{date-1-day}}" + }, + { + "id": "prod-13", + "name": "Образовательные программы", + "description": "Поиск поставщика корпоративного обучения для сотрудников", + "category": "Образование", + "type": "buy", + "companyId": "company-18", + "budget": "до 200 000 руб", + "createdAt": "{{date-6-days}}", + "updatedAt": "{{date-2-days}}" + }, + { + "id": "prod-14", + "name": "Финансовые услуги", + "description": "Требуется консультация по инвестиционному планированию", + "category": "Финансовые услуги", + "type": "buy", + "companyId": "company-19", + "budget": "до 150 000 руб", + "createdAt": "{{date-2-days}}", + "updatedAt": "{{date-1-day}}" + } + ] +} diff --git a/server/routers/procurement/mocks/search.json b/server/routers/procurement/mocks/search.json new file mode 100644 index 0000000..e9081c8 --- /dev/null +++ b/server/routers/procurement/mocks/search.json @@ -0,0 +1,122 @@ +{ + "suggestions": [ + "Строительные материалы", + "Металлоконструкции", + "Логистические услуги", + "Промышленное оборудование", + "Запчасти для спецтехники", + "IT-решения", + "Консалтинговые услуги", + "Пищевая продукция", + "Энергетическое оборудование", + "Медицинские технологии", + "Образовательные услуги", + "Финансовые услуги", + "Сельскохозяйственная техника", + "Торговое оборудование", + "Производственные услуги" + ], + "searchHistory": [ + { + "query": "строительные материалы", + "timestamp": "{{date-1-day}}" + }, + { + "query": "металлоконструкции", + "timestamp": "{{date-2-days}}" + }, + { + "query": "логистические услуги", + "timestamp": "{{date-3-days}}" + }, + { + "query": "IT-решения", + "timestamp": "{{date-5-days}}" + }, + { + "query": "консалтинг", + "timestamp": "{{date-7-days}}" + }, + { + "query": "пищевая продукция", + "timestamp": "{{date-10-days}}" + }, + { + "query": "медицинское оборудование", + "timestamp": "{{date-12-days}}" + }, + { + "query": "образовательные услуги", + "timestamp": "{{date-15-days}}" + }, + { + "query": "финансовые услуги", + "timestamp": "{{date-18-days}}" + }, + { + "query": "сельскохозяйственная техника", + "timestamp": "{{date-20-days}}" + } + ], + "savedSearches": [ + { + "id": "saved-1", + "name": "Строительные компании", + "params": { + "industries": ["Строительство"], + "minRating": 4.5 + }, + "createdAt": "{{date-7-days}}" + }, + { + "id": "saved-2", + "name": "Поставщики металла", + "params": { + "query": "металл", + "industries": ["Производство"] + }, + "createdAt": "{{date-14-days}}" + }, + { + "id": "saved-3", + "name": "IT-компании", + "params": { + "industries": ["IT"], + "minRating": 4.0 + }, + "createdAt": "{{date-21-days}}" + }, + { + "id": "saved-4", + "name": "Логистические услуги", + "params": { + "industries": ["Логистика"], + "companySize": ["100-250", "250-500"] + }, + "createdAt": "{{date-28-days}}" + }, + { + "id": "saved-5", + "name": "Консалтинговые услуги", + "params": { + "industries": ["Услуги"], + "minRating": 4.3 + }, + "createdAt": "{{date-35-days}}" + } + ], + "recommendationReasons": { + "Строительство": "Отличная репутация в строительной сфере", + "Производство": "Высокое качество производимой продукции", + "Логистика": "Надежные логистические решения", + "Торговля": "Широкий ассортимент и быстрые поставки", + "IT": "Инновационные IT-решения", + "Услуги": "Профессиональные консалтинговые услуги", + "Пищевая промышленность": "Качественная пищевая продукция", + "Энергетика": "Энергоэффективные решения", + "Медицина": "Современные медицинские технологии", + "Образование": "Эффективные образовательные программы", + "Финансы": "Надежные финансовые услуги", + "Сельское хозяйство": "Современные агротехнологии" + } +} diff --git a/server/routers/procurement/mocks/user.json b/server/routers/procurement/mocks/user.json new file mode 100644 index 0000000..1d12fb0 --- /dev/null +++ b/server/routers/procurement/mocks/user.json @@ -0,0 +1,13 @@ +{ + "mockUser": { + "id": "user-123", + "email": "test@company.com", + "firstName": "Иван", + "lastName": "Петров", + "position": "Генеральный директор" + }, + "mockTokens": { + "accessToken": "mock-access-token-{{timestamp}}", + "refreshToken": "mock-refresh-token-{{timestamp}}" + } +}